Чи можу я мати кілька первинних ключів в одній таблиці?


Відповіді:


559

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

CREATE TABLE userdata (
  userid INT,
  userdataid INT,
  info char(200),
  primary key (userid, userdataid)
);

Оновлення: Ось посилання з більш детальним описом складених первинних ключів.


2
У цьому прикладі BOTH userid та userdataid потрібні для ідентифікації / пошуку унікальної рядки. Не впевнений, який був задум ОП, але я прийшов сюди, щоб переконатися, чи зможу я однозначно ідентифікувати ряд з одним із наборів ключів. Наприклад, я б хотів ідентифікувати унікального користувача з ім’ям користувача або АБО userid, не потребуючи обох. Я здогадуюсь, відповідь РБ на унікальні індекси зробила б цю справу.
Бурріто

1
@Benitok Як уже згадувалося в РБ. За відповідь , ви можете використовувати унікальні індекси , щоб зробити те , що ви шукаєте (унікальний, що індексується колонки незалежно від інших унікальних, індексованих стовпців на одній і тій же таблиці). Не забудьте проконсультуватися з вашим специфічним смаком посібника SQL для отримання детальної інформації про точний синтаксис мови.
4:00

195

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

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


39

У таблиці може бути кілька ключів-кандидатів. Кожен кандидат-ключ - це стовпчик або набір стовпців, які є УНІКАЛЬНИМИ, взяті разом, а також НЕ NULL. Таким чином, вказуючи значення для всіх стовпців будь-якого ключа-кандидата, достатньо, щоб визначити, що є один рядок, який відповідає критеріям, або взагалі немає рядків.

Ключі кандидата - це фундаментальне поняття в моделі реляційних даних.

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

Я рекомендую такі практики, але в реляційній моделі немає нічого, що вимагало б вибору первинного ключа серед ключових кандидатів.


5
Домовились. У логічній моделі всі клавіші рівні (жодна не є "первинною"). Вибір того, який ключ у фізичній реалізації отримає позначення PRIMARY KEY, залежить від довільності та залежить від постачальника / продукту.
день, коли

3
Я б сказав, що це залежить від дизайнера баз даних.
Вальтер Мітті

Я просто натрапив на випадок використання, коли це потрібно. У мене є таблиця, яка буде створена / керована Entity Framework - яка, наскільки я можу зібрати, на даний момент не підтримує унікальні композиційні обмеження, що не є первинним ключем. Однак він підтримує складені первинні ключі. Дані також будуть пов'язані з віддаленою системою баз даних, яка зовсім не підтримує складені ключі. Я вже створив композитний ПК в EF, але також додав стовп GUID, що не зводиться, і інша система може використовувати для унікальної ідентифікації.
Кріс Невілл

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

Я думаю, що існує вимога, щоб ПК був мінімальним, тобто використовує найменшу кількість стовпців для унікальної ідентифікації кожного запису.
gary

14

Це відповідь як на головне питання, так і на питання @ Кальмі

Який сенс матиме кілька автогенеруючих стовпців?

Цей код нижче має складений первинний ключ. Один з його стовпців автоматично збільшується. Це працюватиме лише в MyISAM. InnoDB генерує помилку " ПОМИЛКА 1075 (42000): Неправильне визначення таблиці; може бути лише один стовпець авто, і він повинен бути визначений як ключ ".

DROP TABLE IF EXISTS `test`.`animals`;
CREATE TABLE  `test`.`animals` (
  `grp` char(30) NOT NULL,
  `id` mediumint(9) NOT NULL AUTO_INCREMENT,
  `name` char(30) NOT NULL,
  PRIMARY KEY (`grp`,`id`)
) ENGINE=MyISAM;

INSERT INTO animals (grp,name) VALUES
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),('mammal','whale'),
    ('bird','ostrich');

SELECT * FROM animals ORDER BY grp,id;

Which returns:

+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+

2
Це працює, якщо спочатку вказати стовпчик з автоматичним збільшенням у визначенні первинного ключа. (Можливо, це змінилося. Я щойно перевірив це в 5.6)
CTarczon

11

(Вивчали їх багато)

Ключі кандидата - Мінімальна комбінація стовпців, необхідна для унікального визначення рядка таблиці.
Складені ключі - 2 і більше стовпців.

  • У таблиці можуть існувати кілька ключів кандидата .
    • Первинний ключ - тільки один з ключів - кандидатів , які обрані нами
    • Альтернативні ключі - всі інші ключі-кандидати
      • Обидва первинного ключа та альтернативні ключі можуть бути складовими ключами

Джерела:
https://en.wikipedia.org/wiki/Superkey
https://en.wikipedia.org/wiki/Candidate_key
https://en.wikipedia.org/wiki/Primary_key
https://en.wikipedia.org / wiki / Compound_key


6

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

Приклад:

Person(id, name, email, street, zip_code, area)

Можлива функціональна залежність між. id -> name,email, street, zip_code and area Але часто а zip_codeасоціюється з areaа, тому між ними існує внутрішня функціональна залежність zip_code -> area.

Таким чином, можна розглянути розділення його на іншу таблицю:

Person(id, name, email, street, zip_code)
Area(zip_code, name)

Так, щоб він відповідав третій нормальній формі .


6

Первинний ключ - це дуже невдале позначення через конотацію "Первинного" та підсвідомої асоціації внаслідок логічної моделі. Таким чином я уникаю його використання. Натомість я посилаюся на сурогатний ключ фізичної моделі та природний ключ (і) логічної моделі.

Важливо, щоб логічна модель для кожного суб'єкта господарювання мала щонайменше один набір "бізнес-атрибутів", які містять ключ для сутності. Бойс, Кодд, Дата та ін називають їх у реляційній моделі як ключі кандидата. Коли ми потім будуємо таблиці для цих організацій, їхні ключі-кандидати стають природними ключами в цих таблицях. Лише за допомогою цих природних ключів користувачі можуть однозначно ідентифікувати рядки в таблицях; оскільки сурогатні ключі завжди повинні ховатися від користувачів. Це тому, що сурогатні ключі не мають ділового значення.

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

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

Зауважте, що ці переваги виникають лише тоді, коли сурогатний ключ є і малим, і кластерним ключем. Якщо GUID використовується як ключ кластеризації, ситуація часто буде гіршою, ніж якби був використаний найменший доступний природний ключ. Якщо таблиця організована як купа, то для пошуку ключів буде використовуватися 8-байтовий (куча) RowID, який кращий за 16-байтовий GUID, але менш ефективний, ніж 4-байтове ціле число.

Якщо GUID потрібно використовувати через обмеження бізнесу, то пошуки кращого кластеризаційного ключа варто. Якщо, наприклад, невеликий ідентифікатор сайту та 4-байтовий "номер послідовності сайту" є можливим, то такий дизайн може дати кращі показники, ніж GUID як сурогатний ключ.

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

Розглянемо цей приклад ::

ALTER TABLE Persons
ADD CONSTRAINT pk_PersonID PRIMARY KEY (P_Id,LastName)

де кортеж " (P_Id, LastName) " вимагає обмеження унікальності, і може бути тривалим Unicode LastName плюс 4-байтовим цілим числом, було б бажано (1) декларативно застосувати це обмеження як " ДОДАТИ КОНСТРУКТУВАННЯ pk_PersonID UNIQUE NONCLUSTERED (P_Id , LastName) "та (2) окремо оголошують маленький сурогатний ключ" первинним ключем "кластерного індексу. Варто зазначити, що Anita, можливо, бажає лише додати LastName до цього обмеження, щоб зробити це закритим полем, яке є непотрібним у кластерному індексі, оскільки всі поля охоплені ним.

Можливість SQL Server призначити Первинний ключ некластеризованим - це прикрою історичною обставиною, пов’язаною зі співвідношенням значення "бажаний природний або кандидатський ключ" (з логічної моделі) зі значенням "ключ пошуку в сховищі" з фізичного Модель. Я розумію, що спочатку SYBASE SQL Server завжди використовував 4-байтовий RowID, чи то в купу, так і в кластерний індекс, як "ключ пошуку в сховищі" з фізичної моделі.


3
Чи можете ви перекладіть це англійською мовою!
Джасір

3

Деякі люди використовують термін "первинний ключ", щоб означати саме цілий стовпець, який отримує свої значення, породжені деяким автоматичним механізмом. Наприклад, AUTO_INCREMENTв MySQL або IDENTITYв Microsoft SQL Server. Ви використовуєте первинний ключ у цьому сенсі?

Якщо так, відповідь залежить від бренду бази даних, яку ви використовуєте. У MySQL цього не можна зробити, ви отримуєте помилку:

mysql> create table foo (
  id int primary key auto_increment, 
  id2 int auto_increment
);
ERROR 1075 (42000): Incorrect table definition; 
there can be only one auto column and it must be defined as a key

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


5
Який сенс матиме кілька автогенеруючих стовпців?
Тарнай Калман

Я не маю на увазі випадку використання, але якщо б колись була потреба, деякі марки баз даних підтримували б це, а деякі - ні. Це все, що я говорю.
Білл Карвін

1
Ось випадок: у таблиці замовлень у мене є ідентифікатор (збільшений автоматично), і зовнішній ідентифікатор (хеш-рядки), обидва повинні бути унікальними, щоб теоретично можна сказати, що вони обидва "первинні". Звичайно, це можна зробити за допомогою вторинного унікального індексу, але все-таки це законний випадок (ІМХО)
Нір

2

Мати два первинні ключі одночасно, неможливо. Але (якщо припустити, що ви не зіпсували корпус складеним ключем), можливо, вам може знадобитися зробити один атрибут унікальним.

CREATE t1(
c1 int NOT NULL,
c2 int NOT NULL UNIQUE,
...,
PRIMARY KEY (c1)
);

Однак зауважте, що у реляційній базі даних «супер ключ» - це підмножина атрибутів, яка однозначно ідентифікує кортеж чи рядок у таблиці. "Ключ" - це "супер ключ", який має додаткову властивість, яка видаляє будь-який атрибут з ключа, робить цей ключ більше не "супер ключем" (або просто "ключ" - це мінімальний супер ключ). Якщо ключів більше, усі вони є ключами-кандидатами. Ми вибираємо один з ключів-кандидатів як основний ключ. Ось чому говорити про декілька первинних ключів для одного відношення чи таблиці - це конфлікт.


У Вікіпедії немає визначення поняття "ключ". Крім того, "видалення будь-якого атрибуту з ключа, робить цей ключ більше не" супер-клавішею ", для мене нічого не означало, так як при видаленні атрибута із супер-ключа все ще може бути супер-ключ.
Манохар Редді Поредді

@ManoharReddyPoreddy Так, у такому випадку ваш набір атрибутів - це не "ключ", а "супер ключ". Що я маю на увазі, якщо набір атрибутів є "ключем", набір повинен бути мінімальним, або набір повинен мати додаткове властивість, що вилучення будь-якого атрибута з набору робить отриманий набір не більше "супер ключем".
Rusiru Adithya Samarasinghe

Схоже, ваше фактичне значення "ключ" - це Candidate_key ( en.wikipedia.org/wiki/Candidate_key ), можливо, це слід так згадати.
Манохар Редді Поредді

@ManoharReddyPoreddy Так. Я вже згадував це у своїй відповіді. "Якщо клавіш більше, усі вони є ключами". Все одно дякую за Ваш відгук.
Rusiru Adithya Samarasinghe

1. Коли ви згадуєте "Якщо є більше ключів, всі вони є ключами-кандидатами", ... Ви маєте на увазі інше / ще, вони не є кандидатурними ключами? ... 2. Де інша частина? ... Ми взагалі одна і та ж сторінка?
Манохар Редді Поредді

1

Первинний ключ - це ключ, який однозначно ідентифікує запис і використовується у всіх індексах. Ось чому ви не можете мати більше одного. Це також загалом ключ, який використовується при приєднанні до дочірніх таблиць, але це не є вимогою. Справжня мета ПК - переконатися, що щось дозволяє вам однозначно ідентифікувати запис, щоб зміни даних впливали на правильний запис і таким чином можна було створювати індекси.

Однак ви можете помістити кілька полів в один первинний ключ (складений ПК). Це зробить ваші приєднання повільнішими (особливо якщо вони є більшими полями рядкових рядків), а ваші індекси збільшаться, але це може усунути необхідність приєднання до деяких дочірніх таблиць, тому що стосується продуктивності та дизайну, прийміть це у випадку, підстава справи. Коли ви це робите, кожне поле саме по собі не є унікальним, але їх поєднання є. Якщо одне або кілька полів у складеному ключі також має бути унікальним, то вам потрібен унікальний індекс на ньому. Імовірно, якщо одне поле є унікальним, це кращий кандидат на ПК.

Зараз часом у вас є більше одного кандидата на ПК. У цьому випадку ви вибираєте ключ як ПК або використовуєте сурогатний ключ (я особисто віддаю перевагу сурогатним ключам для цього випадку). І (це важливо!) Ви додаєте унікальні індекси до кожного з кандидатських ключів, які не були обрані як ПК. Якщо дані мають бути унікальними, йому потрібен унікальний індекс, чи це ПК чи ні. Це питання цілісності даних. (Зверніть увагу, це також справедливо, коли ви використовуєте сурогатний ключ; люди потрапляють у проблеми із сурогатними ключами, оскільки вони забувають створювати унікальні індекси на кандидатських ключах.)

Інколи трапляються випадки, коли потрібно більше одного сурогатного ключа (як правило, ПК, якщо у вас є). У цьому випадку те, що ви хочете, не більше ПК, це більше полів з автогенерованими ключами. Більшість БД цього не дозволяють, але є способи їх обійти. Спочатку подумайте, чи може друге поле обчислюватися на основі першого автогенерованого ключа (наприклад, Field1 * -1) або, можливо, потреба у другому автогенерованому ключі дійсно означає, що вам слід створити пов’язану таблицю. Пов’язані таблиці можуть бути у взаємозв'язку один на один. Ви б застосували це, додавши ПК з батьківської таблиці до дочірньої таблиці, а потім додавши до таблиці нове автоматично сформоване поле, а потім усі поля, які підходять для цієї таблиці. Потім виберіть одну з двох клавіш як ПК, а іншу поставте унікальний індекс (автогенероване поле не повинно бути ПК). І обов’язково додайте FK до поля, яке знаходиться в батьківській таблиці. Загалом, якщо у вас немає додаткових полів для дочірньої таблиці, вам потрібно вивчити, чому ви вважаєте, що вам потрібні два автогенеровані поля.


0

Хороші технічні відповіді були надані краще, ніж я можу зробити. Я лише можу додати до цієї теми:

Якщо ви хочете чогось не дозволеного / прийнятного, це вагомий привід зробити крок назад.

  1. Зрозумійте суть, чому це не прийнятно.
  2. Дізнайтеся більше про документацію / статті журналів / веб тощо
  3. Проаналізуйте / перегляньте поточний дизайн та вкажіть основні недоліки.
  4. Розгляньте і протестуйте кожен крок під час нового дизайну.
  5. Завжди з нетерпінням чекайте і намагайтеся створити адаптивне рішення.

Сподіваюся, що це допоможе комусь.


1
загальна (хоча й корисна) порада, а не відповідь на конкретне питання.
Bradford Needham

-3

Так, це можливо в SQL, але ми не можемо встановити більше одного первинного ключа в MsAccess. Тоді я не знаю про інші бази даних.

CREATE TABLE CHAPTER (
    BOOK_ISBN VARCHAR(50) NOT NULL,
    IDX INT NOT NULL,
    TITLE VARCHAR(100) NOT NULL,
    NUM_OF_PAGES INT,
    PRIMARY KEY (BOOK_ISBN, IDX)
);

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