Чи добре мати зовнішній ключ як первинний ключ?


102

У мене є дві таблиці:

  • Користувач (ім'я користувача, пароль)
  • Профіль (ідентифікатор профілю, стать, дата народження, ...)

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

Мене плутає пропозиція мого друга: мати поле "userId" як зовнішній та первинний ключ і видалити поле "profileId". Який підхід кращий?


3
Entity Framework генерує це (з кодом спочатку) для відносин zeroOrOne (і один)-до-одного. Отже ... можливо. Це найкращий спосіб ... Це вже інше питання. Але це дійсно. Я ніколи цього не робив, створюючи власні бази даних (але навіть ніколи про це не думав).
Raphaël Althaus

Відповіді:


132

Зовнішні ключі майже завжди є "Дозволити дублікати", що робить їх непридатними як Основні ключі.

Натомість знайдіть поле, яке однозначно ідентифікує кожен запис у таблиці, або додайте нове поле (або ціле число, що автоматично збільшується, або GUID), яке буде діяти як первинний ключ.

Єдиним винятком для цього є таблиці з відношенням " один на один" , де зовнішній ключ і первинний ключ пов'язаної таблиці є одним і тим же.


73
Складений первинний ключ, який складається з двох зовнішніх ключів, також чудово підходить для реалізації взаємозв’язків багато-до-багатьох.
праворуч

1
@rezadru: Нехай мені не погодиться з цим, але сурогатний ключ майже завжди є кращим вибором.
Роберт Харві,

4
Ніщо про зовнішній ключ не диктує, що це 1 до багатьох (або "дозволити дублікати", як написано). Ключові обмеження та унікальність - це два окремих поняття в базі даних, і їх можна легко змішати так само просто, як додавання індексу (що було б третім окремим поняттям).
сліпий

40

Первинні ключі завжди повинні бути унікальними, зовнішні ключі повинні дозволяти не унікальні значення, якщо таблиця має відношення "один до багатьох". Цілком чудово використовувати зовнішній ключ як первинний ключ, якщо таблиця пов’язана відношенням «один на один», а не відносинами «один до багатьох». Якщо ви хочете, щоб один і той самий запис користувача мав можливість мати більше 1 відповідного запису профілю, використовуйте окремий первинний ключ, інакше дотримуйтесь того, що у вас є.


11

Так, законним є наявність первинного ключа як зовнішнього. Це рідкісна конструкція, але вона застосовується для:

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

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

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


Якби таблиці часто приєднувались, це могло б мати негативні результати, що призводить до звичайної рекомендації, що 1 таблиця краще. У деяких випадках дані завжди отримують доступ окремо, а не об’єднуються, і організаційні переваги можуть мати наявність двох таблиць із співвідношенням 1: 1.
сліпий

4

Зазвичай вважається поганою практикою мати стосунки один до одного. Це тому, що ви могли б просто представити дані в одній таблиці і досягти того самого результату.

Однак існують випадки, коли ви не зможете внести ці зміни в таблицю, на яку посилаєтесь. У цьому випадку немає проблем із використанням зовнішнього ключа як первинного ключа. Може допомогти наявність складеного ключа, що складається з унікального первинного ключа, що автоматично збільшується, та зовнішнього ключа.

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


2
Я погоджуюся з вами здебільшого, що всі дані в одній таблиці та додаткові стовпці мають багато переваг. Хоча wrt this .. "ви можете просто представити дані в одній таблиці і досягти того самого результату" ..: наявність окремої таблиці може бути корисним, наприклад тут, якщо запис таблиці профілів не є обов'язковим. Наприклад, кожен клієнт банку може не мати реєстрації в Інтернет-банкінгу. У цьому випадку таблиця реєстрації IB може бути використана для обмеження інших таблиць від подальших дочірніх записів. Знову ж таки, тут це можна зробити за допомогою нового ПК для таблиці реєстрації IB.
Тедді,

1
@Teddy Так само я переважно погоджуюся з тим, що ви сказали. Однак у вихідному питанні вони вказують "... його запис профілю створюється автоматично ...", маючи на увазі, що таблиця профілю не є обов'язковою. У ситуації, коли таблиця профілю була необов’язковою, тоді так, якщо вона є окремою таблицею, це можливо. Але знову ж таки, вони могли просто використовувати обнулювані стовпці в одній таблиці.
Tshsmith

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

Абсолютно, але якщо ми об'єднаємо таблиці 1 на 1, ми можемо не допустити доступу людей до третьої (технічно другої зараз) таблиці. Але питання, яке задав ОП, містить рядок "... при реєстрації ... ... його запис профілю створюється автоматично ...", роблячи це зайвим.
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 (ідентифікатор_заказу та item_id) як ПК для цієї таблиці.

Я не фахівець з БД, але якщо ви можете логічно виправдати наявність автоматично згенерованого значення як свого ПК, я б це зробив. Якщо це непрактично, тоді об'єднання двох (або, можливо, більше) FK може служити вашим ПК. Але НЕ я можу придумати жоден випадок, коли одне значення FK може бути виправдане як ПК.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.