Составной первичный ключ

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

в качестве примера рассмотрим следующую таблицу, иллюстрирующую проблему:

----------------------------------------------------------------
| source_id | id_on_source | data                              |
----------------------------------------------------------------
| 1         | 17600        | ...                               |
| 1         | 17601        | ...                               |
| 2         | 1            | ...                               |
| 3         | 1            | ...                               |
----------------------------------------------------------------

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

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

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

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

каковы плюсы и минусы использования составного внешнего ключа в этом случае? Что бы вы предпочли?

8 ответов


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

Я бы создал стандартный автоматически увеличивающийся первичный ключ в вашей таблице источников и добавил уникальный индекс в Столбцах source_id и id_on_source.

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

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


составные клавиши трудно управлять и медленно присоединяться. Поскольку вы создаете сводную таблицу, используйте суррогатный ключ (т. е. столбец autoincrement/identity). Оставьте свои естественные ключевые столбцы там.

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

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


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

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

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


Я считаю, что составные ключи создают очень естественную и описательную модель данных. Мой опыт приходит от Oracle и я не думаю, что есть какие-либо технические проблемы при создании составного ПК. На самом деле любой, кто анализирует словарь данных, сразу же поймет что-то о таблице. В вашем случае было бы очевидно, что каждый source_id должен иметь уникальный id_on_source.

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


практически единственный раз, когда я использовать составной первичный ключ, когда старшая часть ключа является ключом к другой таблице. Например, я могу создать таблицу OrderLineItem с первичным ключом OrderId + LineNumber. Поскольку многие обращения к таблице OrderLineItem будут " order join orderlineitem using (orderid)" или некоторым вариантом этого, это часто удобно. Это также упрощает просмотр дампов базы данных, чтобы выяснить, какие элементы строки подключены к чему порядок.

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

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

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


Я столкнулся с проблемами, используя много составных ключей, и поэтому я бы не рекомендовал его (подробнее ниже), я также обнаружил, что есть преимущества в независимом/суррогатном ключе (а не естественном) при попытке откатить ошибки пользователя. Проблема заключалась в том, что через набор отношений одна таблица соединяла две таблицы, где для каждой строки часть композита была одинаковой (это было уместно в 3 - й нормальной форме-сравнение двух частей родителя). Я де-дублировал эту часть композита отношение в таблице соединений (поэтому вместо parent1ID, other1ID, parent2ID, other2ID был parentID, other1ID, other2ID), но теперь отношение не могло обновлять изменения первичного ключа, потому что оно пыталось сделать это дважды через каждый маршрут и не удалось в середине.


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


хотя uniqueid является хорошим первичным ключом, я согласен, что обычно лучше использовать другой, естественный (не обязательно уникальный) ключ в качестве кластеризованного индекса. Например, если uniqueid-это ПК, который идентифицирует сотрудников, вы можете захотеть, чтобы кластеризованный индекс был отделом (если ваши операторы select обычно извлекают всех сотрудников в данном отделе). Если вы хотите использовать unqiqueid в качестве кластеризованного индекса, см. NEWSEQUENTIALID () функция: это создает последовательные значения uniqueid, которые (будучи последовательными) имеют лучшую производительность кластеризации.


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

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