Первинний ключ або Унікальний індекс?


127

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

Я розробляю нову базу даних для нового проекту, і я маю дилему:

У теорії БД первинний ключ є основним елементом, це нормально, але в проектах REAL які переваги та недоліки обох?

Що ви використовуєте в проектах?

EDIT: ... а як щодо первинних ключів та реплікації на сервері MS SQL?


2
Тут обговорюються деякі додаткові міркування (хоча і з додатковим контекстом покривного індексу) - dba.stackexchange.com/questions/21554/…
StuartLC

ПРИМІТКА: SQLite відрізняється тим, що вони дозволяють первинному ключу бути недійсним, проти загального стандарту через стару проблему. sqlite.org/lang_createtable.html
bitinn

Відповіді:


168

Що таке унікальний індекс?

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

СТВОРИТИ ТАБЛИЦЮ table1 (foo int, bar int);
СТВОРИТИ УНІКАЛЬНИЙ ІНДЕКС ux_table1_foo ON table1 (foo); - Створіть унікальний індекс на foo.

ВСТАВЛЯЄТЬСЯ в таблицю1 (foo, bar) ЦІННОСТІ (1, 2); -- ГАРАЗД
ВСТАВЛЯЄТЬСЯ в таблицю1 (foo, bar) ЦІННОСТІ (2, 2); -- ГАРАЗД
ВСТАВЛЯЄТЬСЯ в таблицю1 (foo, bar) ЦІННОСТІ (3, 1); -- ГАРАЗД
ВСТАВЛЯЄТЬСЯ в таблицю1 (foo, bar) ЦІННОСТІ (1, 4); - Невдачі!

Дублікат запису "1" для ключа "ux_table1_foo"

Остання вставка не вдається, оскільки вона порушує унікальний індекс стовпця, fooколи він намагається вставити значення 1 у цей стовпець вдруге.

У MySQL унікальне обмеження дозволяє кілька NULL.

Можна зробити унікальний покажчик на мутилових стовпцях.

Первинний ключ проти унікального індексу

Те саме:

  • Первинний ключ передбачає унікальний індекс.

Різні речі:

  • Первинний ключ також передбачає НЕ NULL, але унікальний індекс може бути нульовим.
  • Може бути лише один первинний ключ, але може бути кілька унікальних індексів.
  • Якщо не визначений кластерний індекс, первинним ключем буде кластерний індекс.

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

2
@Alexandre Jasmin: Виправлена ​​подяка. Частина про кілька стовпців згадана пізніше.
Марк Байєрс

Посилаючись на нулі, стандарти ansi дозволяють отримати кілька нульових значень у наборі даних з унікальним обмеженням на ньому, і це також реалізація в Oracle та PostgreSQL. Я вважаю, що SQL Server дозволяє лише одне нульове значення.
Девід Олдрідж

3
але все ж я цього не отримав, як, коли використовувати первинний ключ або коли використовувати унікальний індекс? або можуть бути обома в однакових ситуаціях.
Аміт

33

Ви можете бачити це так:

Первинний ключ є унікальним

Унікальне значення не повинно бути представником елемента

Значення ?; Первинний ключ використовується для ідентифікації елемента, якщо у вас є "Person", ви б хотіли мати персональний ідентифікаційний номер (SSN або такий), який є первинним для вашої особи.

З іншого боку, у людини може бути електронна пошта, яка є унікальною, але десятки не ідентифікують її.

У мене завжди є первинні ключі, навіть у таблицях відносин (середня таблиця / таблиця з'єднань) я можу їх мати. Чому? Ну, мені подобається дотримуватися стандарту при кодуванні, якщо "Особа" має ідентифікатор, Автомобіль має ідентифікатор, ну, тоді У Особи -> Авто має бути ідентифікатор!


У таблицях відносин: ви маєте на увазі, що ви вводите новий стовпець зі штучним первинним ключем (наприклад, ціле число) або використовуєте складений первинний ключ (person_id, car_id)?

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

1
Інша річ, яку сурогатний первинний ключ робить для вашої складеної / приєднаної таблиці - це легке обслуговування ручних завдань.
Роберт К. Барт

2
Первинний ключ вам потрібен лише в тому випадку, якщо ви збираєтеся мати дітей. Навіщо додавати стовпчик і послідовність, якщо значення ніде не з’являється, якщо значення використовується ні для чого? Це робота, щоб уникнути доступу Access до ПК. Складіть ПК, якщо вам потрібно ідентифікувати запис у дитини, інакше це марно.

3
Якщо це не має нічого спільного з стосунками, то яке воно стосується? Ти вказуєш на поле і кажеш, що це первинне. І? Тоді що відбувається? І якщо немає природного ПК, я додаю стовпець, послідовність і тригер, і все тому, що ____? Деякі просто повинні бути первинними. Я уникаю правил без причин.

10

Іноземні ключі працюють як з унікальними обмеженнями, так і з первинними ключами. З книг онлайн:

Обмеження ЗОВНІШНЯ КЛЮЧА не повинно бути пов'язане лише з обмеженням ОСНОВНОГО КЛЮЧУ в іншій таблиці; його також можна визначити для посилання на стовпці обмеження UNIQUE в іншій таблиці

Для транзакційної реплікації вам потрібен первинний ключ. З книг онлайн:

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

Обидві відповіді призначені для SQL Server 2005.


ЩО мене лякає пекло (перша цитата). Чому? У мене є таблиця людей з довільним ідентифікатором, це мій ПК, але я вирішую додати Великобританію до телефону, електронної пошти та SSN ... тож тепер 4 різних таблиці приєднуються до людини у 4 різних стовпцях? Я думаю, я б пропустив будь-яку гнучкість, яку ви могли б отримати за послідовність.

5

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

Як приклад, я маю такі таблиці:

CREATE TABLE toll_booths (
    id            INTEGER       NOT NULL PRIMARY KEY,
    name          VARCHAR(255)  NOT NULL,
    ...
    UNIQUE(name)
)

CREATE TABLE cars (
    vin           VARCHAR(17)   NOT NULL PRIMARY KEY,
    license_plate VARCHAR(10)   NOT NULL,
    ...
    UNIQUE(license_plate)
)

CREATE TABLE drive_through (
    id            INTEGER       NOT NULL PRIMARY KEY,
    toll_booth_id INTEGER       NOT NULL REFERENCES toll_booths(id),
    vin           VARCHAR(17)   NOT NULL REFERENCES cars(vin),
    at            TIMESTAMP     DEFAULT CURRENT_TIMESTAMP NOT NULL,
    amount        NUMERIC(10,4) NOT NULL,
    ...
    UNIQUE(toll_booth_id, vin)
)

У нас є дві таблиці сутностей ( toll_boothsі cars) та таблиця транзакцій ( drive_through). У toll_boothтаблиці використовується сурогатний ключ, оскільки він не має природного атрибута, який не гарантується змінювати (ім’я можна легко змінити). У carsтаблиці використовується природний первинний ключ, оскільки він має унікальний ідентифікатор, що не змінюється ( vin). Таблиця drive_throughтранзакцій використовує сурогатний ключ для легкої ідентифікації, але також має унікальне обмеження на атрибути, які гарантовано є унікальними на момент вставлення запису.

http://database-programmer.blogspot.com має кілька чудових статей з цього приводу.


4

Немає недоліків первинних ключів.

Щоб додати лише трохи інформації до відповідей @MrWiggles та @Peter Parker, коли таблиця не має первинного ключа, наприклад, ви не зможете редагувати дані в деяких додатках (вони в кінцевому підсумку говорять, що не можна редагувати / видаляти дані без первинний ключ). Postgresql дозволяє декілька значень NULL знаходитись у стовпці UNIQUE, PRIMARY KEY не дозволяє NULL. Також деякі ORM, які генерують код, можуть мати деякі проблеми з таблицями без первинних ключів.

ОНОВЛЕННЯ:

Наскільки мені відомо, неможливо копіювати таблиці без первинних ключів у MSSQL, принаймні, без проблем ( деталей ).


Існує накладні витрати, коли вставляються нові рядки або оновлюється цей стовпець.

3

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


3
таблиця буде сортована за кластерним індексом не обов’язково за первинним ключем.
Рей Буйсен

1
просто так трапляється, що більшість людей встановлюють своїм основним ключем кластерний індекс.
Рей Буйсен

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

1
Це НЕ ВЖЕ справді погана ідея. Знайте свої дані, знайдіть свої RDBMS, знайте, що означає вибір. Рідко вибір ЗАВЖДИ хороший чи поганий. Якщо б ВИНАГИ була одна, база даних маніпулює її чи забороняє. Вони дають вам вибір, оскільки "Це залежить".

2

На додаток до того, що було сказано в інших відповідях, деякі бази даних та системи можуть вимагати наявності основного. На думку спадає одна ситуація; коли використовується реплікація підприємства з Informix, ПК повинен бути присутній для таблиці для участі у реплікації.


2

Поки ви не дозволяєте NULL для значення, вони повинні оброблятися однаково, але значення NULL обробляється по-різному в базах даних (AFAIK MS-SQL не дозволяє більше ніж одне (1) значення NULL, mySQL і Oracle дозволяють це , якщо стовпець є УНІКАЛЬНИМ) Отже, ви повинні визначити цей стовпець NOT NULL UNIQUE INDEX


1
MS-SQL дозволяє допускати декілька значень NULL у стовпці, що має унікальний індекс, як і кожен RDBMS. Подумайте про це так: NULL не є значенням, тому коли ви вставите другий NULL, він ніколи не буде відповідати існуючому. Вираз (NULL == NULL) не оцінюється як істинне чи хибне, воно оцінюється як NULL.
gregmac

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

2

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

Унікальні індекси не є частиною стандарту SQL. Конкретна реалізація СУБД визначатиме наслідки оголошення унікального індексу.

В Oracle оголошення первинного ключа призведе до створення унікального індексу від вашого імені, тож питання майже не суперечить. Я не можу розповісти вам про інші продукти СУБД.

Я віддаю перевагу оголошенню первинного ключа. Це спричиняє заборону NULL в ключових стовпниках, а також забороняє дублікати. Я також підтримую заяву про обмеження СПРАВКИ для забезпечення цілісності сутності. У багатьох випадках оголошення індексу на coummn (s) іноземного ключа прискорить приєднання. Цей вид індексу взагалі не повинен бути унікальним.


Первинним ключем у MS SQL Server завжди є УНІКАЛЬНО та НЕ NULL - наприклад, це дійсно просто Унікальний індекс, але з доданим обмеженням він не може бути NULL.
marc_s

Oracle може застосувати унікальне обмеження з унікальним індексом. Я був би здивований, якби MSSS не зміг. Сказати, що "це дійсно просто унікальний індекс" - це суперечка.

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

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

2

Є деякі недоліки CLUSTERED INDEXES vs UNIQUE INDEXES.

Як уже було сказано, КЛАСТИРОВАНИЙ ІНДЕКС фізично упорядковує дані в таблиці.

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

У відносно невеликих таблицях це нормально, але, потрапивши до таблиць, у яких є дані про ГБ, і вставники / видалення впливають на сортування, у вас виникнуть проблеми.


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

1

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

Кілька разів я не створю сурогатного ключа, коли у мене є таблиця приєднання, яка бере участь у відносинах «багато-багато». У цьому випадку я оголошую обидва поля як основний ключ.


"Я майже ніколи не створюю таблицю без числового первинного ключа": чому завжди числовий? Первинний ключ не повинен бути числовим (до речі, він також не повинен бути AUTO_INCREMENT).
Hibou57

@ Hinou57, тому що я виявив, що природні ключі рідко є унікальними, і вони майже завжди змінюються. Далі приєднання до інтерджерів, як правило, набагато швидше, ніж приєднання до природних ключів varcahrr або гірших складових ключів. Я б не користувався ними майже все час. Це може залежати від типу інформації, яку ви зберігаєте у вашій базі даних, але в особистому досвіді я виявив, що природні ключі з часом надзвичайно ненадійні.
HLGEM

Дякуємо за відповідь HLGEM. Що ви маєте на увазі з ненадійними? Продуктивність? (Сподіваюся, це не питання надійності в розумінні цілісності даних). Я трохи здивований вашими словами, оскільки я, мабуть, використовуючи цілі клавіші або більш природні ключі, наприклад, короткий VARCHAR, швидше за все, має лише незначну різницю, оскільки хеширование використовується скрізь навіть із самими простими двигунами DB.
Hibou57

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

1

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

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

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

Інші тут згадували реплікацію БД, але я не знаю про це.


0

Унікальний індекс може мати одне значення NULL. Він створює НЕ КЛАСТИРОВАНИЙ ІНДЕКС. Первинний ключ не може містити значення NULL. Це створює КЛАСТИРОВАНИЙ ІНДЕКС.


0

У MSSQL Первинні ключі повинні монотонно зростати для найкращої роботи кластерного індексу. Тому ціле число з вставкою ідентичності краще, ніж будь-який природний ключ, який може не монотонно зростати.


-1

Якби це було до мене ...

Вам потрібно задовольнити вимоги бази даних та ваших програм.

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

Потім ви додасте хоча б ще один унікальний індекс до таблиці, який буде використовуватись вашою програмою. Це був би індекс на Employ_id, або account_id, або customer_id тощо. Якщо можливо, цей індекс не повинен бути складеним індексом.

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

Я все для використання обчислених або функціональних індексів - і рекомендую використовувати їх над складеними індексами. Це дуже просто використовувати індекс функції, використовуючи ту саму функцію в пункті де.

Це стосується ваших вимог до програми.

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

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