Почему составные первичные ключи все еще существуют? - PullRequest
58 голосов
/ 23 марта 2011

Мне поручено перенести базу данных в ERP среднего класса.Новая система использует составные первичные ключи здесь и там, и с прагматической точки зрения, почему?

По сравнению с автоматически генерируемыми идентификаторами я вижу только отрицательные аспекты;

  • Внешние ключистановится размытым
  • Более сложная миграция или дБ-редизайн
  • Негибкость по мере изменения бизнеса.(У моей машины нет reg.plate ..)
  • Та же самая целостность лучше достигается с ограничениями.

Она возвращается к концепции дизайна ключей-кандидатов, которые я тоже не вижу смыслаof.

Это привычка / артефакт из гибких дней (минимизация пробелов / индексов), или я что-то упускаю?

// edit // Только что нашел хороший SO-пост: Составные первичные ключи против уникального поля идентификатора объекта //

Ответы [ 9 ]

57 голосов
/ 23 марта 2011

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

Некоторые примеры из реальной жизни:

  • Таблицы ссылок «многие ко многим», в которых первичные ключи состоят из ключей связанных сущностей.

  • Мультитенантные приложения, когда tenant_id является частью первичного ключа каждого объекта, и объекты могут быть связаны только внутри одного и того же клиента (ограничены внешним ключом).

  • Приложения, обрабатывающие сторонние данные (с уже предоставленными первичными ключами)

Обратите внимание, что логически все это может быть достигнуто с помощью ограничения UNIQUE (дополнительно к суррогату PRIMARY KEY).

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

  • Некоторые системы не позволяют FOREIGN KEY ссылаться на что-либо, кроме PRIMARY KEY.

  • Некоторые системы будут кластеризовать таблицу только на PRIMARY KEY, следовательно, создание составного элемента PRIMARY KEY повысит производительность запросов, объединяющих составные данные.

40 голосов
/ 23 марта 2011

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

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

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

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

40 голосов
/ 23 марта 2011

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

Например (чистый пример - мы здесь не говорим о дизайне БД), допустим, у вас есть таблица ORDER и ORDER_ITEM. Если вы используете ProductId и LineNumber ( UPDATE : и, как Педро упомянул OrderId или даже лучше OrderNumber) в качестве составного первичного ключа в ORDER_ITEM, то в кросс-таблице для SHIPPING, вы можете иметь ProductId в SHIPPING_ORDERITEM. Это может значительно повысить вашу производительность, если, например, у вас кончился этот продукт, и вам нужно найти все продукты этого ProductId, которые должны быть отправлены без необходимости присоединения.

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

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

9 голосов
/ 23 марта 2011

Естественные первичные ключи хрупкие.

Предположим, что мы построили систему на основе естественного PK (CountryCode, PhoneNumber), и через несколько лет нам нужно добавить Extension или заменить PK на один столбец: Email. Если эти столбцы PK распространяются на все дочерние таблицы, это становится очень дорогим.

Несколько лет назад были созданы некоторые системы, предполагавшие, что номер социального страхования является естественным PK, и его пришлось перепроектировать для использования идентификаторов, когда SSN стал неуникальным и обнуляемым.

Поскольку мы не можем предсказать будущее, мы не знаем, приведет ли позднее какое-то изменение к устаревшей модели, которая раньше была совершенно правильной и полной.

8 голосов
/ 23 марта 2011

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

Ex. У вас есть список запчастей, которые вы покупаете у поставщиков. Вы могли бы создать своего поставщика и таблицу запчастей следующим образом:

SUPPLIER
SupplierId
SupplierName

PART
PartId
PartName
SupplierId

Э-э-э Таблица частей позволяет дублировать данные. Поскольку вы использовали суррогатный ключ, который был сгенерирован автоматически, вы не применяете тот факт, что деталь от поставщика должна вводиться только один раз. Вместо этого вы должны создать таблицу PART следующим образом:

PART
SupplierId
SupplierPartId
PartName

В этом примере ваши детали поступают от определенных поставщиков, и вы хотите применить правило: «Один поставщик может поставить только одну деталь один раз» в таблице PARTS. Следовательно, составной ключ. Ваш составной ключ предотвращает случайный повторный ввод детали.

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

8 голосов
/ 23 марта 2011

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

4 голосов
/ 24 марта 2011

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

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

Пример: Система для организации вечеринок в локальной сети

Система поддерживает несколько вечеринок в локальной сети, в которых участвуют одни и те же люди и организаторы:

CREATE TABLE users ( users_id serial PRIMARY KEY, ... );

И существует несколько вечеринок:

CREATE TABLE parties ( parties_id serial PRIMARY KEY, ... );

Но большинство других вещей должны нести информацию о том, с какой стороной они связаны:

CREATE TABLE ticket_types (
    ticket_types_id serial,
    parties_id integer REFERENCES parties,
    name text,
    ....
    PRIMARY KEY(ticket_types_id, parties_id)
);

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

CREATE TABLE attendances (
    attendances_id serial,
    parties_id integer REFERENCES parties,
    ticket_types_id integer,
    PRIMARY KEY (attendances_id, parties_id),
    FOREIGN KEY (ticket_types_id, parties_id) REFERENCES parties
);
4 голосов
/ 23 марта 2011

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

Если, например, у вас есть таблица, содержащая данные о транспортном средстве, применение суррогатного VehicleId абстрагирует то, что значит быть транспортным средством с точки зрения данных.Когда вы ссылаетесь на VehicleId = 1, вы наверняка говорите о каком-то транспортном средстве, но знаем ли мы, что это Chevy Impala 2008 года или Ford F-150 1991 года?Нет. Могут ли базовые данные любого транспортного средства № 1 изменяться в любое время?Да.

2 голосов
/ 23 марта 2011

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

  • Многие ко многим таблицам соединения. Они обычно требуют уникального ключа в паре ключей в любом случае. В некоторых случаях дополнительные ключи могут быть включены в ключ.
  • Слабые дочерние таблицы. Такие вещи, как строки заказа, не стоят сами по себе. В этом случае я использую первичный ключ родительской (заказы) таблицы в составной таблице.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...