Суррогатные против натуральных / бизнес ключей
Здесь мы идем снова, старый аргумент все еще возникает...
Будет ли лучше иметь бизнес-ключ в качестве первичного ключа или лучше использовать суррогатный идентификатор (т. Е. Идентификатор SQL Server) с уникальным ограничением на поле бизнес-ключа?
Пожалуйста, предоставьте примеры или доказательства в поддержку вашей теории.
19 ответов
И то и другое. Возьми свой пирог и съешь его.
Помните, что в первичном ключе нет ничего особенного, кроме того, что он помечен как таковой. Это не более, чем ограничение NOT NULL UNIQUE, и таблица может иметь более одного.
Если вы используете суррогатный ключ, вы все равно хотите, чтобы бизнес-ключ гарантировал уникальность в соответствии с бизнес-правилами.
Несколько причин использовать суррогатные ключи:
Стабильность: изменение ключа из-за деловой или естественной потребности негативно повлияет на связанные таблицы. Суррогатные ключи редко, если вообще когда-либо, нужно менять, потому что нет значения, связанного со значением.
Соглашение: позволяет вам иметь стандартизированное соглашение об именах столбцов первичного ключа, а не думать о том, как объединять таблицы с различными именами для их PK.
Скорость: в зависимости от значения и типа PK суррогатный ключ целого числа может быть меньше, быстрее индексировать и искать.
Похоже, что никто еще ничего не сказал в поддержку несуррогатных (я не решаюсь сказать "естественных") ключей. Так что здесь идет...
Недостатком суррогатных ключей является то, что они бессмысленны (некоторые считают это преимуществом, но...). Это иногда вынуждает вас присоединять к вашему запросу гораздо больше таблиц, чем это действительно необходимо. Для сравнения:
select sum(t.hours)
from timesheets t
where t.dept_code = 'HR'
and t.status = 'VALID'
and t.project_code = 'MYPROJECT'
and t.task = 'BUILD';
против:
select sum(t.hours)
from timesheets t
join departents d on d.dept_id = t.dept_id
join timesheet_statuses s on s.status_id = t.status_id
join projects p on p.project_id = t.project_id
join tasks k on k.task_id = t.task_id
where d.dept_code = 'HR'
and s.status = 'VALID'
and p.project_code = 'MYPROJECT'
and k.task_code = 'BUILD';
Разве кто-нибудь всерьез считает, что следующая идея - хорошая идея?
select sum(t.hours)
from timesheets t
where t.dept_id = 34394
and t.status_id = 89
and t.project_id = 1253
and t.task_id = 77;
"Но, - скажет кто-то, - что произойдет, когда код для MYPROJECT, VALID или HR изменится?" На что мой ответ будет: "зачем вам это менять?" Это не "естественные" ключи в том смысле, что какой-то внешний орган собирается издать закон о том, что впредь "ДЕЙСТВИТЕЛЬНО" следует перекодировать как "ХОРОШО". Лишь небольшой процент "естественных" ключей действительно попадает в эту категорию - обычными примерами являются SSN и Zip-код. Я бы определенно использовал бессмысленный цифровой ключ для таблиц, таких как Person, Address - но не для всего, что, по некоторым причинам, большинство людей здесь защищают.
Смотрите также: мой ответ на другой вопрос
Суррогатный ключ НИКОГДА не будет иметь причины для изменения. Я не могу сказать то же самое о естественных ключах. Фамилии, электронные письма, номера ISBN - все они могут измениться за один день.
Суррогатные ключи (как правило, целые числа) имеют дополнительную ценность, заключающуюся в том, чтобы сделать ваши табличные отношения более быстрыми и более экономичными с точки зрения хранения и скорости обновления (что еще лучше, внешние ключи не нужно обновлять при использовании суррогатных ключей, в отличие от полей бизнес-ключей, которые меняются сейчас и потом).
Первичный ключ таблицы должен использоваться для уникальной идентификации строки, главным образом для целей объединения. Подумайте о персоне: имена могут меняться, и они не гарантированно уникальны.
Мыслимые компании: вы - счастливая компания Merkin, которая сотрудничает с другими компаниями в Merkia. Вы достаточно умны, чтобы не использовать название компании в качестве первичного ключа, поэтому вы используете уникальный идентификатор компании правительства Merkia, состоящий из 10 буквенно-цифровых символов. Затем Merkia меняет идентификационные данные компании, потому что они думали, что это будет хорошей идеей. Ничего страшного, вы используете функцию каскадных обновлений вашего db-движка для изменений, которые не должны вас привлекать. Позже ваш бизнес расширяется, и теперь вы работаете с компанией во Фридонии. Идентификатор компании Freedonian - до 16 символов. Вам необходимо увеличить первичный ключ идентификатора компании (также поля внешнего ключа в Заказах, Выпусках, MoneyTransfers и т. Д.), Добавив поле Страна в первичном ключе (также во внешних ключах). Ой! Гражданская война во Фридонии разделилась на три страны. Название страны вашего сотрудника должно быть изменено на новое; каскадные обновления на помощь. Кстати, каков твой первичный ключ? (Страна, Идентификатор компании) или (Идентификатор компании, Страна)? Последний помогает объединениям, первый избегает другого индекса (или, возможно, многих, если вы хотите, чтобы ваши Заказы также группировались по странам).
Все это не является доказательством, но указывает на то, что суррогатный ключ для уникальной идентификации строки для всех целей, включая операции соединения, предпочтительнее бизнес-ключа.
Я ненавижу суррогатные ключи в целом. Их следует использовать только при отсутствии качественного натурального ключа. Когда вы думаете об этом, абсурдно думать, что добавление бессмысленных данных в вашу таблицу может улучшить ситуацию.
Вот мои причины:
При использовании естественных ключей таблицы группируются так, как их чаще всего ищут, что ускоряет запросы.
При использовании суррогатных ключей необходимо добавлять уникальные индексы в столбцы логических ключей. Вы все еще должны предотвратить логическое дублирование данных. Например, вы не можете разрешить две организации с одинаковыми именами в вашей таблице организации, даже если pk является столбцом суррогатного идентификатора.
Когда в качестве первичного ключа используются суррогатные ключи, гораздо менее понятно, каковы естественные первичные ключи. При разработке вы хотите знать, какой набор столбцов делает таблицу уникальной.
В цепочках отношений один ко многим цепочки логических ключей. Так, например, в организациях есть много счетов, а в счетах много счетов. Таким образом, логический ключ организации - OrgName. Логический ключ Учетных записей - OrgName, AccountID. Логическим ключом Invoice является OrgName, AccountID, InvoiceNumber.
Когда используются суррогатные ключи, цепочки ключей усекаются, имея только внешний ключ для непосредственного родителя. Например, таблица Invoice не имеет столбца OrgName. У него есть только столбец для AccountID. Если вы хотите искать счета для данной организации, вам нужно присоединиться к таблицам Организация, Учетная запись и Счет. Если вы используете логические ключи, то вы можете запросить таблицу организации напрямую.
Хранение значений суррогатного ключа таблиц поиска приводит к тому, что таблицы заполняются бессмысленными целыми числами. Для просмотра данных необходимо создать сложные представления, объединяющие все таблицы поиска. Таблица поиска предназначена для хранения набора допустимых значений для столбца. Его не следует кодифицировать, храня вместо этого целочисленный суррогатный ключ. В правилах нормализации нет ничего, что предлагало бы хранить суррогатное целое число вместо самого значения.
У меня есть три разные базы данных книг. Ни один из них не показывает использование суррогатных ключей.
Я хочу поделиться с вами своим опытом этой бесконечной войны:D на естественной и суррогатной ключевой дилемме. Я думаю, что как суррогатные ключи (искусственные автоматически сгенерированные), так и естественные ключи (составленные из столбцов с доменным значением) имеют свои плюсы и минусы. Поэтому, в зависимости от вашей ситуации, может быть более уместным выбрать тот или иной метод.
Поскольку многие люди представляют суррогатные ключи как почти идеальное решение, а естественные ключи - как чуму, я остановлюсь на аргументах другой точки зрения:
Недостатки суррогатных ключей
Суррогатными ключами являются:
- Источник проблем с производительностью:
- Они обычно реализуются с использованием автоматически увеличиваемых столбцов, что означает:
- Обход к базе данных каждый раз, когда вы хотите получить новый Id (я знаю, что это можно улучшить с помощью алгоритмов кэширования или [seq]hilo, но все же эти методы имеют свои недостатки).
- Если однажды вам понадобится переместить ваши данные из одной схемы в другую (по крайней мере, в моей компании это происходит довольно регулярно), то у вас могут возникнуть проблемы с коллизиями Id. И да, я знаю, что вы можете использовать UUID, но для того, чтобы это длилось, требуется 32 шестнадцатеричных числа! (Если вы заботитесь о размере базы данных, это может быть проблемой).
- Если вы используете одну последовательность для всех ваших суррогатных ключей, то - наверняка - вы получите конфликт в вашей базе данных.
- Они обычно реализуются с использованием автоматически увеличиваемых столбцов, что означает:
- Ошибка склонна. Последовательность имеет ограничение max_value, поэтому, как разработчик, вы должны обратить внимание на следующие моменты:
- Вы должны циклически повторять вашу последовательность (когда достигается максимальное значение, оно возвращается к 1,2,...).
- Если вы используете последовательность как порядок (со временем) ваших данных, то вы должны обработать случай цикличности (столбец с Id 1 может быть новее, чем строка с Id max-value - 1).
- Убедитесь, что ваш код (и даже ваши клиентские интерфейсы, которые не должны происходить так, как это должно было бы быть внутренним идентификатором), поддерживает целые числа 32b/64b, которые вы использовали для хранения значений последовательности.
- Они не гарантируют не дублированные данные. Вы всегда можете иметь 2 строки с одинаковыми значениями столбца, но с другим сгенерированным значением. Для меня это проблема суррогатных ключей с точки зрения дизайна базы данных.
- Больше в Википедии...
Мифы о природных ключах
- Составные ключи менее неэффективны, чем суррогатные ключи. Нет! Это зависит от используемого движка базы данных:
- Естественные ключи не существуют в реальной жизни. Извините, но они существуют! Например, в авиационной промышленности следующий кортеж всегда будет уникальным в отношении заданного регулярного рейса (авиакомпания, flightDate, номер рейса, операционный суффикс). В более общем смысле, когда набор бизнес-данных гарантированно является уникальным по данному стандарту, тогда этот набор данных является [хорошим] подходящим кандидатом.
- Естественные ключи "загрязняют схему" дочерних таблиц. Для меня это больше чувство, чем настоящая проблема. Наличие первичного ключа из 4 столбцов по 2 байта каждый может быть более эффективным, чем один столбец из 11 байтов. Кроме того, 4 столбца можно использовать для непосредственного запроса к дочерней таблице (используя 4 столбца в предложении where) без присоединения к родительской таблице.
Заключение
Используйте естественные ключи, когда это уместно, и используйте суррогатные ключи, когда их лучше использовать.
Надеюсь, что это помогло кому-то!
Всегда используйте ключ, который не имеет делового значения. Это просто хорошая практика.
РЕДАКТИРОВАТЬ: Я пытался найти ссылку на него в Интернете, но я не мог. Однако в "Паттернах корпоративной архитектуры" [Фаулер] есть хорошее объяснение того, почему вы не должны использовать ничего, кроме ключа, не имеющего никакого значения, кроме как быть ключом. Это сводится к тому, что у него должна быть одна работа и только одна работа.
Суррогатные ключи очень удобны, если вы планируете использовать инструмент ORM для обработки / генерации ваших классов данных. Хотя вы можете использовать составные ключи с некоторыми из более продвинутых картографов (читай: hibernate), это добавляет некоторую сложность вашему коду.
(Конечно, пуристы базы данных будут утверждать, что даже понятие суррогатного ключа является мерзостью.)
Я фанат использования uids для суррогатных ключей, когда это необходимо. Основным преимуществом их является то, что вы знаете ключ заранее, например, вы можете создать экземпляр класса с идентификатором, который уже установлен и гарантированно будет уникальным, в то время как, скажем, с целочисленным ключом вам потребуется значение по умолчанию 0 или -1 и обновите до подходящего значения при сохранении / обновлении.
UID имеют штрафы с точки зрения поиска и скорости соединения, хотя это зависит от желаемого приложения.
На мой взгляд, лучше использовать суррогатный ключ, поскольку вероятность его изменения практически отсутствует. Почти все, что я могу придумать, что вы можете использовать в качестве естественного ключа, может измениться (отказ от ответственности: не всегда верно, но обычно).
Примером может служить БД автомобилей - на первый взгляд вы можете подумать, что номерной знак можно использовать в качестве ключа. Но их можно изменить, чтобы это было плохой идеей. Вы действительно не захотите узнать об этом после выпуска приложения, когда кто-то приходит к вам, желая узнать, почему он не может сменить номерной знак на свой блестящий новый персонализированный.
Случай 1: Ваша таблица является справочной таблицей с менее чем 50 типами (вставками)
Используйте бизнес / натуральные ключи. Например:
Table: JOB with 50 inserts
CODE (primary key) NAME DESCRIPTION
PRG PROGRAMMER A programmer is writing code
MNG MANAGER A manager is doing whatever
CLN CLEANER A cleaner cleans
...............
joined with
Table: PEOPLE with 100000 inserts
foreign key JOBCODE in table PEOPLE
looks at
primary key CODE in table JOB
Случай 2: Ваш стол - это стол с тысячами вставок
Используйте суррогатные / автоинкрементные ключи. Например:
Table: ASSIGNMENT with 1000000 inserts
joined with
Table: PEOPLE with 100000 inserts
foreign key PEOPLEID in table ASSIGNMENT
looks at
primary key ID in table PEOPLE (autoincrement)
В первом случае:
- Вы можете выбрать всех программистов в таблице PEOPLE без использования объединения с таблицей JOB, но только с помощью: "SELECT * FROM PEOPLE WHERE JOBCODE = 'PRG'"
Во втором случае:
- Ваши запросы к базе данных выполняются быстрее, потому что ваш первичный ключ является целым числом
- Вам не нужно беспокоиться о поиске следующего уникального ключа, потому что сама база данных дает вам следующий автоинкремент.
Я считаю, что в сценарии хранилища данных лучше следовать суррогатному ключевому пути. Две причины:
- Вы независимы от исходной системы, и изменения там - такие как изменение типа данных - не затронут вас.
- Вашему DW понадобится меньше физического пространства, так как вы будете использовать только целочисленные типы данных для своих суррогатных ключей. Также ваши индексы будут работать лучше.
Всегда используйте один столбец, суррогатный ключ, если это вообще возможно. Это делает объединения, а также вставляет / обновляет / удаляет намного чище, потому что вы несете ответственность только за отслеживание одного фрагмента информации для поддержания записи.
Затем при необходимости составьте свои бизнес-ключи как уникальные ограничения или индексы. Это сохранит целостность данных.
Бизнес-логика / естественные ключи могут изменяться, но физический ключ таблицы никогда не должен изменяться.
Суррогатные ключи могут быть полезны, когда деловая информация может измениться или быть идентичной. В конце концов, названия компаний не обязательно должны быть уникальными по всей стране. Предположим, вы имеете дело с двумя компаниями под названием Smith Electronics, один в Канзасе и один в Мичигане. Вы можете различить их по адресу, но это изменится. Даже государство может измениться; Что делать, если Smith Electronics из Канзас-Сити, штат Канзас, переходит через реку в Канзас-Сити, штат Миссури? Не существует очевидного способа отличить эти предприятия от естественной ключевой информации, поэтому суррогатный ключ очень полезен.
Думайте о суррогатном ключе как о номере ISBN. Обычно вы определяете книгу по названию и автору. Тем не менее, у меня есть две книги под названием "Перл-Харбор" от HP Willmott, и это определенно разные книги, а не просто разные издания. В таком случае я мог бы сослаться на внешний вид книг или более ранних по сравнению с более поздними, но я также должен использовать ISBN.
Это один из тех случаев, когда суррогатный ключ почти всегда имеет смысл. В некоторых случаях вы выбираете, что лучше для базы данных или для вашей объектной модели, но в обоих случаях лучше использовать бессмысленный ключ или GUID. Это делает индексацию проще и быстрее, и это идентичность вашего объекта, которая не меняется.
Напоминаем, что не рекомендуется размещать кластеризованные индексы на случайных суррогатных ключах, то есть GUID, которые читают XY8D7-DFD8S, поскольку SQL Server не имеет возможности физически сортировать эти данные. Вместо этого вы должны поместить уникальные индексы в эти данные, хотя может быть также полезно просто запустить SQL Profiler для операций с основной таблицей и затем поместить эти данные в помощник по настройке ядра СУБД.
Смотрите ветку @ http://social.msdn.microsoft.com/Forums/en-us/sqlgetstarted/thread/27bd9c77-ec31-44f1-ab7f-bd2cb13129be
В случае базы данных на определенный момент времени лучше всего использовать комбинацию суррогатных и натуральных ключей. Например, вам необходимо отслеживать информацию о члене клуба. Некоторые атрибуты члена никогда не меняются. например, дата рождения, но имя может измениться. Поэтому создайте таблицу Member с суррогатным ключом member_id и создайте столбец для DOB. Создайте другую таблицу с именем person name и имейте столбцы для member_id, member_fname, member_lname, date_updated. В этой таблице естественным ключом будет member_id + date_updated.
Лошадь для курсов. Чтобы заявить о моей предвзятости; Сначала я разработчик, поэтому я в основном заинтересован в том, чтобы предоставить пользователям работающее приложение.
Я работал над системами с естественными ключами, и мне пришлось потратить много времени, чтобы убедиться, что изменения значений будут иметь место.
Я работал на системах только с суррогатными ключами, и единственным недостатком было отсутствие денормализованных данных для разделения.
Большинству традиционных разработчиков PL/SQL, с которыми я работал, не нравились суррогатные ключи из-за количества таблиц в соединении, но наши тестовые и производственные базы данных никогда не вызывали проблем; дополнительные объединения не влияли на производительность приложения. В случае с диалектами базы данных, которые не поддерживают такие предложения, как "X внутреннее соединение Y для Xa = Yb", или разработчиками, которые не используют этот синтаксис, дополнительные объединения для суррогатных ключей затрудняют чтение запросов, а также их более длительный ввод и ввод. проверьте: см. сообщение Тони Эндрюса. Но если вы используете ORM или любую другую среду генерации SQL, вы не заметите этого. Сенсорный набор также смягчает.
Может быть, не совсем относится к этой теме, но у меня болит голова с суррогатными ключами. Предварительно предоставленная аналитика Oracle создает автоматически сгенерированные SK на всех своих таблицах измерений в хранилище, а также сохраняет их на основе фактов. Таким образом, в любое время, когда они (измерения) необходимо перезагружать при добавлении новых столбцов или заполнении для всех элементов в измерении, SK, назначенные во время обновления, делают SK не синхронизированными с исходными значениями, сохраненными в факте, заставляя полная перезагрузка всех таблиц фактов, которые к нему присоединяются. Я бы предпочел, чтобы даже если SK был бессмысленным числом, был бы какой-то способ, которым он не мог бы измениться для оригинальных / старых записей. Как многие знают, нестандартные решения редко служат потребностям организации, и нам приходится постоянно их настраивать. Теперь у нас есть хранилище данных за 3 года, и полные перезагрузки из систем Oracle Financial очень велики. Так что в моем случае они не генерируются при вводе данных, а добавляются в хранилище, чтобы помочь составить отчет о производительности. Я понимаю, но наши меняются, и это кошмар.