Можно ли использовать внешний ключ в качестве первичного ключа?


103

У меня две таблицы:

  • Пользователь (логин, пароль)
  • Профиль (profileId, пол, дата рождения, ...)

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

Меня смущает предложение моего друга: иметь поле «userId» в качестве внешнего и первичного ключа и удалить поле «profileId». Какой подход лучше?


3
Entity Framework генерирует это (сначала с кодом) для отношений ноль или один (и один) к одному. Так что ... это возможно. Это лучший способ ... Это другой вопрос. Но это действительно так. Я никогда этого не делал при создании собственных баз данных (но даже не думал об этом).
Raphaël Althaus

Ответы:


132

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

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

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


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

1
@rezadru: Я не могу не согласиться с правым фолдингом, но суррогатный ключ почти всегда лучший выбор.
Роберт Харви

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

40

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


11

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

  • соотношение 1: 1. Две таблицы нельзя объединить в одну, потому что разные разрешения и привилегии применяются только на уровне таблицы (по состоянию на 2017 год такая база данных была бы нечетной).

  • соотношение 1: 0..1. Профиль может существовать или не существовать, в зависимости от типа пользователя.

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


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

4

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

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

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


2
В основном я согласен с вами в том, что наличие всех данных в одной таблице в качестве дополнительных столбцов дает множество преимуществ. Хотя в отношении этого ... "вы могли бы просто представить данные в одной таблице и достичь того же результата" ..: наличие отдельной таблицы может быть полезно, например, здесь, если запись в таблице профиля является необязательной. Например, у каждого клиента банка может не быть регистрации в интернет-банке. В этом случае таблица регистрации IB может использоваться для ограничения других таблиц от дополнительных дочерних записей. Опять же, здесь это можно сделать и с новым PK для таблицы регистрации IB.
Teddy

1
@Teddy.Также я в основном согласен с тем, что вы сказали. Однако в исходном вопросе они заявляют: «... его запись профиля создается автоматически ...», подразумевая, что таблица профиля не является дополнительной. В ситуации, когда таблица профиля была необязательной, то да, можно было бы сделать ее отдельной таблицей. Но опять же, они могли просто использовать столбцы, допускающие значение NULL, в той же таблице.
Tshsmith

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

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

Моя основная причина рассмотрения этого заключается в том, что в хранилищах данных рекомендуется разделять таблицы фактов и измерений. Отдельные таблицы фактов и измерений служат полезными подсказками при работе с таким программным обеспечением, как PowerPivot, PowerBI и Tableau.
Марко Росас,

4

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


2
это полезно также для проектирования супертипа-подтипа. Первичный ключ таблиц подтипов должен быть ссылкой внешнего ключа на таблицу супертипов.
axelioo

2

Я бы не стал этого делать. Я бы сохранил profileIDпервичный ключ таблицыProfile

Внешний ключ - это просто ссылочное ограничение между двумя таблицами

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

http://www.aisintl.com/case/primary_and_foreign_key.html


1
Возможно - если у вас есть ограничение FK между User.UserID и Profile.UserID, тогда настоятельно рекомендуется иметь индекс для Profile.UserID. Почему бы не сделать этот первичный кластеризованный индекс в таблице Profile, сохранив второй индекс и много ненужной работы для механизма базы данных?
Обратный инженер

1

Это зависит от бизнеса и системы.

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


0

Краткий ответ: ЗАВИСИТ .... В данном конкретном случае все может быть хорошо. Однако эксперты рекомендуют не делать этого практически каждый раз; включая ваш случай.

Зачем?

Ключи редко бывают уникальными в таблицах, если они являются чужими (происходят из другой таблицы) по отношению к рассматриваемой таблице. Например, идентификатор элемента может быть уникальным в таблице ITEMS, но не в таблице ORDERS, поскольку такой же тип элемента, скорее всего, будет существовать в другом порядке. Аналогичным образом, идентификаторы заказов могут быть уникальными (могут) в таблице ORDERS, но не в какой-либо другой таблице, такой как ORDER_DETAILS, где может существовать заказ с несколькими позициями, и для запроса определенного элемента в определенном порядке вам необходимо объединение двух FK (order_id и item_id) как PK для этой таблицы.

Я не эксперт по БД, но если вы можете логически обосновать наличие автоматически сгенерированного значения в качестве вашего ПК, я бы это сделал. Если это нецелесообразно, то объединение двух (или может быть больше) FK может служить вашим ПК. НО, я не могу представить ни одного случая, когда одно значение FK может быть оправдано как PK.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.