составные первичные ключи это плохая практика? [закрыто]


14

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

Мой вопрос основан на этой статье

ошибки проектирования баз данных

Часть о составных первичных ключах:

Плохая практика № 6: составные первичные ключи

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

Изображение составного первичного ключа

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

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


4
Это не «хорошая» или «плохая» практика. Каждое дизайнерское решение должно служить цели; если вы можете объяснить (себе и другим), почему вам нужен композитный ПК, вы можете идти. И наоборот, если вы можете объяснить, почему вам это не нужно, вам тоже пора. На мой взгляд, статья, на которую вы ссылаетесь, очень плохо объясняет.
mustaccio

эта статья озвучивает точку, но если мы посмотрим популярные фреймворки (например, rails) в своих «лучших практиках», не поддерживающих этот тип первичных ключей, поэтому я спросил почему? это из-за технических трудностей или чего-то еще.
Хакван

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

1
Чем GUID лучше, чем INTEGER [Serial | Авто_Инкремент | Личность | <what_integer_you_like>]?
Верас

4
Я бы не стал нанимать этого автора
папараццо

Ответы:


31

Сказать, что использование "Composite keys as PRIMARY KEY is bad practice"это полная ерунда!

Композиты PRIMARY KEYчасто являются очень «хорошей вещью» и единственным способом моделирования естественных ситуаций, возникающих в повседневной жизни!

Подумайте о классическом учебном примере Базы данных-101 для студентов и курсов, а также о многих курсах, которые посещают многие студенты!

Создать таблицы курса и ученика:

CREATE TABLE course
(
  course_id SERIAL,
  course_year SMALLINT NOT NULL,
  course_name VARCHAR (100) NOT NULL,
  CONSTRAINT course_pk PRIMARY KEY (course_id)
);


CREATE TABLE student
(
  student_id SERIAL,
  student_name VARCHAR (50),
  CONSTRAINT student_pk PRIMARY KEY (student_id)
);

Я приведу пример на диалекте PostgreSQLMySQL ) - должен работать на любом сервере с небольшим количеством настроек.

Теперь, вы , очевидно , хотите , чтобы отслеживать, какой студент принимает какой курс - так у вас есть то , что называется joining table(также называемой linking, many-to-manyили m-to-nтаблица). Они также известны как associative entitiesна более техническом жаргоне!

1 курс может иметь много студентов.
1 студент может пройти много курсов.

Итак, вы создаете объединяющий стол

CREATE TABLE course_student
(
  cs_course_id INTEGER NOT NULL,
  cs_student_id INTEGER NOT NULL,

  -- now for FK constraints - have to ensure that the student
  -- actually exists, ditto for the course.

  CREATE CONSTRAINT cs_course_fk FOREIGN KEY (cs_course_id) REFERENCES course (course_id),
  CREATE CONSTRAINT cs_student_fk FOREIGN KEY (cs_student_id) REFERENCES student (student_id)
);

Теперь, единственный способ разумно придать этому столу PRIMARY KEY- сделать KEYэто комбинацией курса и ученика. Таким образом, вы не можете получить:

  • дубликат студента и комбинации курса

    • на курс может быть зачислен один и тот же студент один раз, и

    • студент может записаться на один и тот же курс только один раз

  • у вас также есть готовый поиск KEYпо курсу для каждого студента - AKA индекс покрытия ,

  • тривиально найти курсы без студентов и студентов, которые не посещают курсы!

    - В примере db-fiddle ограничение PK свернуто в CREATE TABLE - Это можно сделать любым способом. Я предпочитаю иметь все в выражении CREATE TABLE.


ALTER TABLE course_student 
ADD CONSTRAINT course_student_pk 
PRIMARY KEY (cs_course_id, cs_student_id);

Теперь вы могли бы, если обнаружили, что поиск ученика по курсу был медленным, использовать UNIQUE INDEXon (sc_student_id, sc_course_id).

ALTER TABLE course_student 
ADD CONSTRAINT course_student_sc_uq  
UNIQUE (cs_student_id, cs_course_id);

Там нет нет серебряной пули для добавления индексов - они будут делать INSERTс и UPDATES медленнее, но на большой выгоду чрезвычайно убывающиеSELECT раз! Разработчик должен решить индексировать, учитывая их знания и опыт, но говорить, что составные PRIMARY KEYs всегда плохи, просто неправильно.

В случае объединения таблиц они обычно являются единственными, PRIMARY KEY которые имеют смысл! Присоединение к столам также очень часто является единственным способом моделирования того, что происходит в бизнесе или на природе, или практически во всех сферах, которые я могу придумать!

Этот ПК также используется в качестве covering indexускорения поиска. В этом случае было бы особенно полезно, если бы кто-то регулярно проводил поиск (course_id, student_id), что, как можно себе представить, часто имело бы место!

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

Пример из моей собственной работы!

Рассмотрим таблицу полетов, содержащую flight_id, список аэропортов вылета и прилета и соответствующее время, а также таблицу cab_crew с членами экипажа!

Только разумный способ это может быть смоделировано, чтобы иметь таблицу flight_crew с flight_id и crew_id как и атрибуты объявления единственными разумным PRIMARY KEY, чтобы использовать составной ключ из двух полей!


2
в примере курса и студентов, возможно ли для course_student иметь idпервичный ключ и уникальный индекс cs_student_id cs_course_idи иметь те же результаты?
hackvan

2
Зачем тратить ресурсы на это? С PK (course_id, student_id) по определению у вас уже есть уникальный индекс для этих полей! Уникальный индекс на (student_id, course_id) может быть полезен для ускорения поиска - скажем, если вы искали студентов, которые не посещали какие-либо курсы, но это решение могло бы быть оперативным, но в наши дни относительно дешевого хранилища, Я бы рекомендовал это, особенно если учесть, что таблица не будет обновляться очень часто.
17

1
Согласитесь полностью для таблиц ссылок - я работаю с несколькими прямо сейчас. Однако, когда я надеваю шляпу C #, я работаю с генератором reversepoco и создаю полезные классы (поиск, сохранение и т. Д.) Для следующего слоя. Я столкнулся с серьезной проблемой - составные ключи становятся PITA для любого общего кода сохранения / поиска. Да, может быть, я мог бы вернуться к файлам EDMX, но мне все еще нужно поработать либо с кодом особого случая (счетчик столбцов Pkey?), Либо добавить искусственный суррогатный ключ (не нравится и требуются дополнительные ограничения уникальности :(). Итак, я думаю, люди, не похожие на композиты, говорят из кода уровня приложения
Ричард Гриффитс

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

Что происходит, когда студент повторяет курс? Тогда, если курсы, разделенные по времени, не получат разные идентификаторы - тогда у вас есть еще одна таблица сопоставления. Или добавьте поле для даты курса, которое теперь должно быть добавлено к ключу.
iheanyi

3

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

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

Microsoft SQL Server имеет отличительную, но связанную особенность «кластеризованного индекса», который управляет физическим хранением данных в порядке индекса, а также используется внутри других индексов. По умолчанию первичный ключ создается как кластеризованный индекс, но вместо него можно выбрать некластеризованный, предпочтительно после создания кластеризованного индекса. Таким образом, вы можете иметь сгенерированный целочисленный столбец в качестве кластерного индекса и, скажем, имя файла nvarchar (128 символов) в качестве первичного ключа. Это может быть лучше, потому что ключ кластеризованного индекса узок, даже если вы сохраняете имя файла как термин внешнего ключа в других таблицах - хотя этот пример является хорошим примером для того, чтобы не делать этого.

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

https://www.techopedia.com/definition/5547/primary-key описывает пример выбора, сохранять ли данные с номером социального страхования клиента в качестве ключа клиента во всех таблицах данных, или генерировать произвольный customer_id, когда вы зарегистрировать их. На самом деле, это серьезное злоупотребление SSN, независимо от того, работает он или нет; это личная и конфиденциальная ценность данных.

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

Кроме того, у вас возникли проблемы, если SSN или другой ключ данных были записаны неправильно, поэтому у вас неверное значение в 20 ограниченных таблицах, а не только в «Заказчике». В то время как синтетический customer_id не имеет внешнего значения, поэтому он не может быть неправильным значением.


1
Я особенно ценю замечание о том, что в зависимости от данных клиента в качестве ключа даже известные уникальные данные клиента (в данном случае SSN) ломаются, если эти данные когда-либо необходимо исправить.
ToolmakerSteve
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.