Создание вторичного первичного ключа в базе данных для некоторых таблиц


22

В некоторые из моих таблиц я хочу добавить «second_primary_key», который будет uuid или какой-нибудь случайный длинный ключ. Мне это нужно, потому что для некоторых таблиц я не хочу предоставлять целые числа своему веб-приложению. То есть на странице "/ invoices" у меня есть список счетов и ссылка на "/ invoices /: id", где: id - это целое число. Я не хочу, чтобы пользователь знал, сколько там счетов в моей системе, поэтому вместо "/ invoices / 123" я хочу использовать его "second_primary_key", чтобы URL был "/ invoices / N_8Zk241vNa"

То же самое касается других таблиц, где я хочу скрыть реальный идентификатор.

Интересно, это обычная практика? Какой лучший способ реализовать это?

И как в конце концов называется эта техника, так что я делаю поиск по ней?


20
Почему бы не избавиться от целого числа вообще?
Ларсбе

4
Вы можете определить столько уникальных ключей / индексов, сколько вам нужно для таблицы.
abuzittin gillifirca

2
Возможно, вам следует назвать его вторичным ключом-кандидатом. «Первичный» предлагает только один.
Уолтер Митти

4
«Второй основной» - оксюморон. У вас есть первичный ключ, и вы можете иметь вторичные ключи.
Стоп Harm Моника

7
@RobbieDee есть веские причины не полностью нормализовать базу данных. И наличие кандидата или вторичного ключа не совсем дублирует данные.
Мачадо

Ответы:


0

Вы можете добавить столбец UUID, но вам действительно не нужно (и не должно). Это проблема уровня представления. Вы не мечтали бы сказать, храня валютную ценность как 1 999 $ так же как 1999.

Вам просто нужен какой-то способ скрыть ценность приложения на лету. Вы можете сделать это в самом приложении или в виде базы данных.

Поскольку мы говорим только об одном значении, возможно, рассмотрим двухстороннее шифрование, такое как AES или подобное, - чем легче, тем лучше.

Хэширование может быть другой возможностью - это зависит от того, хотите ли вы вернуть номер счета-фактуры, поскольку хэширование - это один из способов.


48

Наличие «альтернативного первичного ключа» является хорошо известным понятием в моделировании реляционных баз данных, его называют «альтернативным ключом», а иногда и «вторичным ключом». Набор «потенциальных первичных ключей» называется «ключами-кандидатами». См. Https://beginnersbook.com/2015/04/alternate-key-in-dbms/

Как вы реализуете это полностью зависит от вас, особенно если вы хотите скрыть общее количество записей. Не существует «наилучшего способа», вы должны проверить свои требования, такие как разрешенный или полезный набор символов, максимальную длину, если вы хотите, чтобы идентификаторы чувствительны к регистру или нет, если вы хотите, чтобы они были читаемыми в распечатанном счете, должен иметь возможность перепечатать их на телефоне без ошибок и тд.


11
Я также видел термины Натуральный ключ и Суррогатный ключ, используемые для описания этого сценария.
DanK

2
@Dari: вы спросили «как называется эта техника» - жирным шрифтом. И если расшифровка AES - возможно, на лету - производит ключи того типа, который вы ищете, используйте его, что не противоречит моему ответу.
Док Браун

1
@Dari Потому что это добавляет совершенно ненужную нагрузку на приложение
Lamak

1
@RobbieDee Мы уже поняли, что вам не нравятся альтернативные ключи, но это не значит, что они бесполезны. Мне нравится подход guid, потому что он упрощает множество проблем.
Т. Сар - Восстановить Монику

1
@RobbieDee Мы не используем SQL Server. Мы используем MySql. И это происходит потому, что кто-то создаст что-то на Prod, скажем, с ID 1234. На Dev, естественно, мы создаем гораздо больше сущностей, чем на Prod. 1234 год назад был взят какой-то одноразовой сущностью для тестирования. Когда нам нужно протестировать сущность из prod, мы должны перенести ее обратно в Dev - и ее первичный ключ уже используется. Миграция намного проще, если ссылки на эту сущность основаны на guid. Но hibernate работает намного лучше с первичным ключом int или long, поэтому мы сохраняем это. Мои разработчики не ленивы и не невежественны - они опытные.
CorsiKa

9

Большинство счетов-фактур имеют номер счета-фактуры, который по большинству правил бухгалтерского учета должен быть последовательным, или бухгалтер может не подписывать результаты по итогам года, или IRS (или аналогичный в вашей стране) может захотеть провести полный аудит ваших вкладок.

Пользователь может определить по номеру счета-фактуры, сколько клиентов вы обслуживали или сколько времени прошло до того, как вы изменили стратегию нумерации счетов.

Сколько счетов хранится в базе данных, не является показателем общей суммы ваших счетов. Есть и другие способы выяснить это, в том числе запросить ваши отчеты за год в Торговой палате.

Однако я бы заблокировал счет-фактуру за экраном входа пользователя в систему, поэтому не каждый может его запросить. Затем при входе пользователя в систему они могут использовать методологию ajax для запроса своих неоплаченных счетов и т. Д. Это защищает ваши данные, скрывает URL-адрес с помощью ajax (обычно никто не удосуживается взглянуть на детали того, как создается запрос ajax). и вы контролируете, как данные отображаются и предлагаются.


7
Обычная стратегия, используемая в банковском деле (с номерами чеков), состоит в том, чтобы не начинать добавочный счет с 1, а скорее с большего числа по этой точной причине.
DanK

Я думаю, именно поэтому идентификатор должен быть дополнительным первичным ключом, а не заменой старого первичного ключа.
Александр

1
Я бы не назвал это первичным ключом. Я бы выбрал слаг, UUID в качестве имени, но по сути это просто еще одно проиндексированное поле в таблице. ID цитаты, номер счета, что угодно. Это поле, но не первичный ключ. Первичный ключ должен быть уникальным и может использоваться внутри системы для реляционного отображения. Если поле проиндексировано, его можно быстро найти с помощью запроса where. userXveryY.where ( 'INVOICE_NUMBER', 'foobarbaz10') получаем ().
Чаллака

1
Вы отвечаете на технический вопрос аргументом, что он не нужен из-за особенностей США (требуются последовательные номера счетов, отчеты в Торговой палате). ИМО это не очень хорошо отвечает на вопрос.
RemcoGerlich

7

Возможно, вы сможете использовать хеш-коды для этого, он предназначен именно для этого сценария.

Он закодирует идентификатор вашей базы данных в короткий хэш (аналог URL-адреса видео на YouTube) и не потребует добавления дополнительных ключей к вашей таблице.


2
Имя несколько вводит в заблуждение, так как это не хеш, а обратимая функция. Но, похоже, это идеальное решение проблемы.
Безумный

2
@CrazyYoghurt Правда ... они назвали причину, по которой назвали ее так, как здесь: hashids.org/#why-hashids
Эрик Кинг,

3

Вы можете создать другой уникальный ключ, но не должны. Не по указанной причине. Есть более простые способы скрыть размеры таблицы.

Хранение N_8Zk241vNaстоит 12 байт на строку в таблице и даже больше в индексе. Это довольно расточительно для того, что вам нужно.

Шифрование целого числа idне требует места и практически ничего не значит во время выполнения. Как вы это сделаете, зависит от вашего языка программирования и / или вашей базы данных.

Обратите внимание, что с AES вы получите 128-битное целое число, что означает 22 символа в base64, вероятно, больше, чем вы хотите. Шифр с размером блока 64, такой как DES или 3DES, дает вам 11 символов, как вы хотите.

Используйте разные ключи для разных таблиц.

Если все, что вам нужно, это скрыть размеры таблиц, вы можете использовать общую последовательность для всех таблиц. Обратите внимание, что это может быть узким местом, если во многих ваших таблицах есть частые вставки. С чем-то вроде Hibernate и алгоритма Hi-Lo эта проблема исчезает.


Точно - хранить это значение просто для того, чтобы скрыть другое, просто неправильно.
Робби Ди

В этом сценарии это может сработать, поскольку идентификатор счета-фактуры не является на самом деле конфиденциальным, но, как правило, использование конфиденциальных идентификаторов в качестве реляционной структуры в базе данных вызовет головную боль, если вам необходимо замаскировать данные в будущем. Лучше относиться к ним как к атрибуту.
DanK

как я могу подать заявку здесь?
Дари

@Dari Как вы можете применить AES к чему-либо ? Не зная вашего языка, никто не может сказать. Обычно AES работает с a byte[], вы можете записать свои idчетыре или восемь байтов, добавить уникальный номер таблицы и зашифровать (входные данные должны быть ровно 16 байтов). Если есть режимы на выбор, ЕЦБ прав.
Maaartinus

@DanK Что? Вы утверждаете, что AES небезопасен? Не зная ключа, злоумышленник ничего не сможет сделать лучше, чем для сохраненного атрибута. Ничего такого. +++ Я думаю, я не понимаю ваш комментарий.
Maaartinus

0

ИМХО создание двух разных первичных ключей невозможно. Конечно, вы можете поместить этот uuid в БД, чтобы он был «псевдонимом» для текущего первичного ключа. Вы можете поместить индекс над этим столбцом с уникальным ограничением, но первичный ключ (по своей сути) является единым в одной таблице. Может быть составной первичный ключ, но это не то, что вы ищете.

Поэтому я предлагаю поместить его туда, но только с указателем. Вы можете создать компонент обработки для запроса данных по PK, а также по другим уникальным столбцам. При обработке запроса "/ invoices / ..." просто проверьте параметр - если он целочисленный, ищите идентификатор, иначе ищите uuid. Или вы можете использовать поиск uuid как запасной вариант, когда поиск по идентификатору ничего не нашел.

И о генерации некоторых «случайных» uuids: почему бы не что-то вроде «взять ID, добавить CONSTANT, преобразовать в шестнадцатеричное». Уникальность идентификатора обеспечит уникальность uuid, шестнадцатеричное число труднее читать для обычных смертных + добавление константы позволит избежать использования uuid, например 00000001.


1
«Почему бы не что-то вроде« взять ID, добавить CONSTANT, преобразовать в шестнадцатеричное »- потому что это довольно легко выяснить - дайте мне URL, и я посмотрю на все другие счета в системе. ИМО не проблема что это на самом деле решает, только те, которые он потенциально может создать
CompuChip

« При обработке запроса для« / invoices / ... »просто проверьте параметр - если он целочисленный, выполните поиск по идентификатору , в противном случае выполните поиск по uuid. « Весь смысл (как я понимаю вопрос) состоит в том, чтобы запретить кому-либо выполнять поиск по идентификатору ( /invoices/123, /invoices/124, ...) так что вы будете искать только по UUID из URL.
TripeHound

Кроме того, не все шестнадцатеричные числа содержат буквы. Было бы невозможно всегда различать ваши основные целые числа и ваши сгенерированные шестнадцатеричные числа.
TRiG

@CompuChip, как я ожидаю, вы интересуетесь компьютерами :-), так что вы узнаете шестнадцатеричное число на первый взгляд. Но Q был написан таким образом, чтобы не показывать номер счета-фактуры напрямую, чтобы другие знали, сколько существует счетов-фактур. Когда я покажу какой-нибудь шестнадцатеричный номер моей жене, матери, соседу ... они не узнают, что это за "странный текст". Если в Q появится уведомление о проблеме безопасности в соответствии с номерами счетов, я бы предложил для этой цели какой-нибудь сложный метод хеширования.
Ярд

@TripeHound, он все еще может искать по внутреннему идентификатору или в какой-либо точке входа с ограниченным доступом ...
Jarda

0

Если оба ключа указывают на один и тот же факт, и они никогда не столкнутся. Почему бы не извлечь другой ключ из исходного, используя некую скалярную функцию, которая создаст пользовательский хэш-код вашего исходного ключа.

В качестве альтернативы вы можете создать таблицу сопоставления приложений, в которой будут храниться обе версии ключа. эта таблица будет действовать как словарь для поиска вторичного ключа.

Насколько я понимаю, ключи - это неявные индексы, и чем больше вы добавляете индексы, тем медленнее будут вставки.


+1 Да, добавление потенциально большого строкового столбца с индексом определенно не является операцией без значения, которую предлагают другие. Помимо объема памяти, по мере добавления индексов скорость вставки начинает ухудшаться.
Робби Ди

0

Другой подход для вашего конкретного случая использования состоит в том, что вместо изменения базы данных и приложения вы можете просто создать собственный маршрут к счетам, чтобы / invoices /: f (id), где f (id), была некоторой функцией идентификатора.

Пользовательский маршрут отвечает за сопоставление запроса с правильным действием на стороне сервера.


0

Это вполне приемлемая практика, также называемая «Альтернативный ключ» (АК). По сути, AK - это еще один уникальный индекс или уникальное ограничение.

Вы даже можете создать ограничения внешнего ключа на основе вашего AK.

Возможный вариант использования подобен тому, что вы объяснили: у вас есть кластеризованный PK на постоянно увеличивающемся идентификационном номере, но вы не хотите, чтобы этот номер отображался или использовался в качестве критерия поиска, потому что его можно просто угадать. Кроме того, у вас есть случайный уникальный идентификатор или ссылочный номер в виде AK, и это тот идентификатор, который вы предоставляете пользователю.


0

Есть несколько видов ключей / индексов. Первичный ключ - это специальный уникальный индекс, и, как показывают ответы, вы, безусловно, можете создать еще один уникальный ключ. И я согласен, что лучше не раскрывать внутренности вашей базы данных, если на то нет веской причины.

Поскольку этот вопрос относится к счетам и номерам, возможно, стоит изучить, как бухгалтерская отрасль ожидает, что номера счетов будут выглядеть: http://smallbusiness.chron.com/assign-invoice-numbers-52422.html

Может показаться беспорядочным наличие внутреннего идентификатора, который является первичным ключом, и другого уникального поля с видимым номером счета приложения / клиента. Но это не так нечисто, когда, скажем, год спустя, клиент хочет принять новую схему нумерации счетов. В этом случае вы бы не мешали внутреннему идентификатору и его отношениям в других таблицах перенумеровать весь шарик воска. Вы бы сохранили свой внутренний идентификатор как есть и изменили бы номер внешнего счета.

В идеале вы стараетесь не связывать таблицы вместе по ключам / внешним ключам, которые могут измениться, и сохраняете свои внутренние таблицы и отношения прозрачными для уровня приложения.


0

Действуй.

Это не отличается от поля "slug", которое часто встречается в статьях блога и тому подобном - уникальный способ ссылки на запись базы данных отдельно от первичного ключа, пригодный для использования в URL. Я никогда не слышал, чтобы кто-то спорил против них.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.