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


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.