Какова наилучшая практика для первичных ключей в таблицах?

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

  1. столбец Identity integer, который автоматически увеличивается.
  2. уникальный идентификатор (GUID)
  3. короткий символ(x) или целое число (или другой относительно небольшой числовой тип) столбец, который может служить столбцом идентификатора строки

номер 3 будет использоваться для достаточно небольшой поиск, в основном чтение таблиц, которые могут иметь уникальный код строки статической длины или числовое значение, такое как год или другое число.

по большей части все остальные таблицы будут иметь автоматически увеличивающееся целое число или уникальный первичный ключ идентификатора.

Вопрос :-)

недавно я начал работать с базами данных, которые не имеют согласованного идентификатора строки, а первичные ключи в настоящее время кластеризованы по различным столбцам. Некоторые примеры:

  • datetime / character
  • datetime / integer
  • datetime / varchar
  • char/nvarchar / nvarchar

есть ли действительный случай для этого? Я бы всегда определил столбец идентификатора или уникального идентификатора для этих случаев.

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

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

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

21 ответов


Я следую нескольким правилам:

  1. первичные ключи должны быть как надо. Предпочитайте числовой тип, поскольку числовые типы хранятся в гораздо более компактном формате, чем символьные форматы. Это связано с тем, что большинство первичных ключей будут внешними ключами в другой таблице, а также использоваться в нескольких индексах. Чем меньше ключ, тем меньше индекс, тем меньше страниц в кэше вы будете использовать.
  2. первичные ключи никогда не должны меняться. Обновление первичного ключа всегда должно быть из Вопрос. Это связано с тем, что он, скорее всего, будет использоваться в нескольких индексах и использоваться как внешний ключ. Обновление одного первичного ключа может вызвать эффект пульсации изменений.
  3. не используйте "первичный ключ вашей проблемы" в качестве первичного ключа логической модели. Например, номер паспорта, номер социального страхования или номер контракта сотрудника, поскольку эти "первичный ключ" могут изменяться для реальных ситуаций.

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


Natural verses artifical keys-это своего рода религиозная дискуссия среди сообщества баз данных - см. в этой статье и другие, на которые он ссылается. Я не сторонник всегда имея искусственные ключи, ни никогда имея их. Я бы решил на индивидуальной основе, например:

  • штаты США: я бы пошел на state_code ('TX' для Техаса и т. д.), а не state_id=1 для Texas
  • сотрудники: обычно я создаю исскуственный ид_сотрудника, поскольку трудно найти ничего, что работает. SSN или эквивалент могут работать, но могут быть проблемы, такие как новый столяр, который еще не предоставил свой SSN.
  • история зарплаты сотрудника: (employee_id, start_date). Я бы не создайте искусственный employee_salary_history_id. Какой смысл это будет служить (кроме "глупой последовательности")

везде, где используются искусственные ключи, вы также должны объявить уникальные ограничения для естественных ключей. Например, используйте state_id, если необходимо, но тогда вам лучше объявить уникальное ограничение на state_code, иначе вы обязательно в конечном итоге получите:

state_id    state_code   state_name
137         TX           Texas
...         ...          ...
249         TX           Texas

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

предположим, у нас есть эти таблицы и столбцы:

Company:
  CompanyId   (primary key)

CostCenter:
  CompanyId   (primary key, foreign key to Company)
  CostCentre  (primary key)

CostElement
  CompanyId   (primary key, foreign key to Company)
  CostElement (primary key)

Invoice:
  InvoiceId    (primary key)
  CompanyId    (primary key, in foreign key to CostCentre, in foreign key to CostElement)
  CostCentre   (in foreign key to CostCentre)
  CostElement  (in foreign key to CostElement)

в случае, если последний бит не имеет смысла, Invoice.CompanyId Это одна из двух внешних ключей, один CostCentre таблицы и один к CostElement таблица. Первичный ключ (InvoiceId, CompanyId).

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

чем меньше шансов облажаться, тем лучше.


Я избегаю использовать естественные ключи по одной простой причине - человеческая ошибка. Хотя часто доступны естественные уникальные идентификаторы (SSN, VIN, номер счета и т. д.), они требуют, чтобы человек вошел в них правильно. Если вы используете SSNs в качестве первичного ключа, кто-то транспонирует пару чисел во время ввода данных, и ошибка не обнаруживается сразу, то вы столкнулись с изменением первичного ключа.

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


нет проблем в создании вашего первичного ключа из разных полей, это Естественный Ключ.

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

Это старая дискуссия. Я предпочитаю суррогатные ключи в большинстве ситуаций.

но нет никакого оправдания для отсутствия ключа.

RE: EDIT

Да, есть много спор об этом: D

Я не вижу никакого очевидного преимущества на естественных ключах, кроме того, что они являются естественным выбором. Вы всегда будете думать Имя, SocialNumber - или что - то в этом роде-вместо idPerson.

суррогатные ключи-это ответ на некоторые из проблем, которые имеют естественные ключи (например, распространение изменений).

когда вы привыкаете к суррогатам, он кажется более чистым и управляемым.

но в конце концов, вы узнаете, что это просто вопрос вкуса - или мышления -. Люди "думают лучше" с естественными ключами, а другие нет.


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

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

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


Что особенного в первичном ключе?

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

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

  • в сторону: к сожалению, побочный эффект большинства разрабатываемых баз данных и разработан программистами приложений (которые я иногда) является что лучше для приложений или приложений часто приводы выбор первичного ключа для таблиц. Это приводит к число и Ключи GUID (поскольку они просты в использовании для фреймворков приложений) и монолитные конструкции таблиц (по мере того как эти уменьшают число применения объекты framework, необходимые для представления данных в памяти). Эти решения по проектированию баз данных, управляемые приложениями, приводят к значительным данным проблемы согласованности при использовании в масштабе. Каркас приложения разработанный таким образом, естественно, привести к таблице в то время конструкций. Создаются "частичные записи" в таблицах и данных, заполненных с течением времени. Взаимодействие нескольких таблиц избегается или при использовании вызывает несогласованность данные, когда приложение работает неправильно. Эти конструкции ведут к данным, которые бессмысленны (или трудно понять), распространение данных над таблицами (вы должны посмотреть на другие таблицы, чтобы понять текущая таблица) и дублированные данные.

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

также было сказано, что первичные ключи никогда не должны меняться по мере обновления основного о ключе не может быть и речи. Но update - это то же самое, что delete с последующей вставкой. По этой логике, вы не должны удалять запись из таблицы с одним ключом, а затем добавить другую запись со вторым ключом. Добавление суррогатного первичного ключа не устраняет тот факт, что другой ключ в таблице существует. Обновление непервичного ключа таблицы может уничтожить значение данных, если другие таблицы зависят от этого значения через суррогатный ключ (например, таблица состояния с суррогатный ключ, имеющий описание статуса, измененное с "обработано" на "отменено", определенно повредит данные). Что всегда должно быть вне вопроса уничтожает смысл данных.


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

просто процитировать несколько моментов:

разработчик должен применить несколько правил при выборе первичного ключа для каждой таблицы:

  • первичный ключ должен однозначно идентифицировать каждую запись.
  • значение первичного ключа записи не может быть null.
  • первичный ключ-значение должно существовать, когда создается запись.
  • первичный ключ должен оставаться стабильным-вы не можете изменить первичный ключ поле(с).
  • первичный ключ должен быть компактным и содержать как можно меньше атрибутов.
  • значение первичного ключа изменить нельзя.

естественные ключи (как правило) нарушает правила. Суррогатные ключи соответствуют правилам. (Вам лучше прочитать эту статью, это стоит вашего времени!)


естественный ключ, если он доступен, обычно лучше всего. Итак, если datetime / char однозначно определяет строку, и обе части имеют значение для строки, это здорово.

Если только datetime имеет смысл, и символ просто прикреплен, чтобы сделать его уникальным, то вы можете просто пойти с полем идентификации.


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

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

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


Я подозреваю, что свернутая газетная терапия Стивена А. Лоу необходима для дизайнера исходной структуры данных.

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


вы должны использовать "составной" или "составной" первичный ключ, который состоит из нескольких полей.

Это вполне приемлемое решение, go здесь для получения дополнительной информации :)


Я тоже всегда использую столбец numeric ID. В oracle я использую number(18,0) без реальной причины выше number (12,0) (или что-то вроде int, а не long), возможно, я просто не хочу беспокоиться о получении нескольких миллиардов строк в БД!

Я также включаю созданный и измененный столбец (тип timestamp) для базового отслеживания, где это кажется полезным.

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


Я ищу естественные первичные ключи и использую их там, где могу.

Если нет естественных ключей, я предпочитаю GUID INT++, потому что SQL Server использует деревья, и плохо всегда добавлять ключи в конец в деревьях.

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

поскольку мне повезло использовать SQL Server, я могу изучить планы выполнения и статистику с помощью профилировщика и анализатора запросов и узнать, как мои ключи работают очень легко.


Я всегда использую поле autonumber или identity.

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


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

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


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


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

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

также некоторый код на построении guids гребня в среда SQL находится в Uniqueidentifier vs identity(архиве).


вот мое собственное правило больших пальцев, на котором я остановился после 25+ лет опыта развития.

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

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


мы делаем много соединений, и составные первичные ключи только что стали свиньей производительности. Простой int или long решает многие проблемы, даже если вы вводите второй ключ кандидата, но гораздо проще и понятнее присоединиться к одному полю против трех.


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

  • идентификатор строки (GUID)
  • Creator (string; имеет значение по умолчанию для имени текущего пользователя (SUSER_SNAME() в T-SQL))
  • Создано (DateTime)
  • метка

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

между тем, фактический PK, если это возможно, является естественным ключом. Мои внутренние правила что-то типа:

  • люди - используйте суррогатный ключ, например INT. Если он внутренний, идентификатор GUID пользователя Active Directory является приемлемым выбор
  • таблицы поиска (например, StatusCodes) - используйте короткий код CHAR; его легче запомнить, чем INTs, и во многих случаях бумажные формы и пользователи также будут использовать его для краткости (например, Status = "E" для "Expired", "A" для "Approved", "NADIS" для "No Asbestos Detected In Sample")
  • связывание таблиц-комбинация FKs (например,EventId, AttendeeId)

так идеально вы заканчиваете вверх с естественным, людск-читаемым и памятным PK, и ORM-содружественным ОДН-ID-в-таблицей идентификатор GUID.

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