Чи є гарною практикою завжди мати первинний ключ з цілим числом автоматичного збільшення?


191

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

Це вважається поганою ідеєю? Чи є якісь недоліки зробити це таким чином? Іноді у мене будуть кілька індексів, наприклад, id, profile_id, subscriptionsде idунікальний ідентифікатор, profile_idпосилання на зовнішню idчастину Profileтаблиці тощо.

Або є сценарії, коли ви не хочете додавати таке поле?


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

24
@ArukaJ Справа в тому, що вона просочує деяку інформацію про систему. Наприклад, припустимо, що база даних містить написані користувачем публікації, кожен з яких отримує послідовний ідентифікатор. Скажімо, ви створюєте чотири повідомлення, кожне з яких отримує ідентифікатор: о 4 ранку (20), 5 ранку (25), 8 вечора (100) та 21 вечора (200). Переглянувши ідентифікатори, ви можете побачити, що лише 4 повідомлення було додано між 4 та 5 ранку, а 100 додано між 20:00 та 21:00. Якщо ви намагалися вибрати час для атаки відмови у службі, це може бути цінною інформацією.
Джошуа Тейлор

29
Для всіх, хто скаржиться на "німецьку проблему з цистернами" .... якщо єдине, що не дозволяє комусь отримати доступ до даних, вони не повинні - це ключ у вашій URL ... у вас є більші проблеми, ніж GUID проти Auto INT.
Метью Віт

11
@MatthewWhited Справа не лише в заміні параметрів у URL-адресі. Припустимо, ви використовуєте веб-сайт і створюєте актив 100 одночасно t, а об’єкт 120 - одночасно t + 60. Якщо ви можете бачити обидва ці ідентифікатори (100 та 120) у безперешкодному вигляді, тепер ви знаєте загальну кількість наявних активів, а також приблизно швидкість, за якою вони створені. Це витік інформації. Це не чисто гіпотетично.
Кріс Хейс

15
"Чи завжди це завжди добре ..." Ні
brian_o

Відповіді:


137

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

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


11
Ось що я роблю. Більшість людей або використовують "id" або "tablename_id" (наприклад, user_id). Аргумент, як правило, не потрібен, якщо стовпець потрібен, але яким способом його назвати.
гросмайстерB

103
Особисто я думаю, що назва таблиці має означати решту. TableName.idна противагу TableName.TableName_idтому, що ще про idщо йдеться? Якщо у мене є інше поле ідентифікатора в таблиці, тоді я приставлю його до імені таблиці, якщо воно стосується якоїсь іншої таблиці
AJJ

10
@ArukaJ Ви згадали, що використовуєте SQLite. Це насправді трохи особливий випадок, оскільки він завжди робить такий стовпчик "під капотом". Таким чином, ви навіть не використовуєте зайвого простору, оскільки отримуєте його, хочете ви цього чи ні. Крім того, рядок SQLite завжди є 64-бітовим цілим числом. Якщо моє розуміння цього є правильним, якщо ви визначаєте рядок автоматичного збільшення, це буде псевдонімом внутрішнього рядка. Так що, можливо, завжди було це робити! Дивіться sqlite.org/autoinc.html
GrandmasterB

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

4
@GrandmasterB: Поточна версія SQLite дозволяє створювати WITHOUT ROWIDтаблиці (з явним PRIMARY KEY) як оптимізацію. Але в іншому випадку INTEGER PRIMARY KEYстовпець є псевдонімом для рядка.
dan04

92

Я не згоден з усіма відповідями раніше. Існує багато причин, чому погано ідея додавати поле автоматичного збільшення у всі таблиці.

Якщо у вас є таблиця, де немає очевидних ключів, поле для автоматичного збільшення здається гарною ідеєю. Зрештою, цього не хочеш select * from blog where body = '[10000 character string]'. Ви б скоріше select * from blog where id = 42. Я б заперечував, що в більшості випадків те, що ви дійсно хочете, - це унікальний ідентифікатор; не є послідовним унікальним ідентифікатором. Напевно, замість цього ви хочете використовувати універсальний унікальний ідентифікатор.

У більшості баз даних є функції для генерування випадкових унікальних ідентифікаторів ( uuidу mysql, postgres. newidВ mssql). Вони дозволяють генерувати дані в безлічі баз даних, на різних машинах, у будь-який час без мережевого з'єднання між ними, і все ще об’єднувати дані з нульовими конфліктами. Це дозволяє легше налаштувати кілька серверів і навіть центри обробки даних, як, наприклад, за допомогою мікросервісів.

Це також дозволяє уникнути зловмисників, які вгадують URL-адреси на сторінки, до яких вони не повинні мати доступ. Якщо є, https://example.com/user/1263мабуть, є https://example.com/user/1262також. Це може дозволити автоматизацію використання безпеки на сторінці профілю користувача.

Також є чимало випадків, коли колонка uuid є марною або навіть шкідливою. Скажімо, у вас є соціальна мережа. Є usersстіл і friendsстіл. Таблиця друзів містить два користувальницькі стовпці та поле з автоматичним збільшенням. Ви хочете 3дружити 5, тому ви вставляєте 3,5в базу даних. База даних додає ідентифікатор автоматичного збільшення та зберігає 1,3,5. Якось користувач знову 3натискає кнопку "додати друга". Ви знову вставляєте 3,5в базу даних, база даних додає ідентифікатор автоматичного збільшення та вставляє 2,3,5. Але зараз 3і 5дружите один з одним двічі! Це марна трата місця, і якщо ви задумаєтесь, то стовпці з автоматичним збільшенням. Все , що вам потрібно , щоб побачити , якщо aіbє друзями - це вибрати для рядка ці два значення. Вони разом є унікальним ідентифікатором рядків. (Ви, ймовірно , хочете зробити написати деяку логіку , щоб переконатися , що 3,5і 5,3є дедупліцірованнимі.)

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

TL; DR: використовуйте UUID замість автоматичного збільшення, якщо у вас ще немає унікального способу ідентифікації кожного рядка.


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

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

11
Ще один сценарій, коли UUID будуть кращими: реалізація невмілої операції PUT, щоб можна було безпечно повторювати запити, не вводячи повторюваних рядків.
yurez

21
У точці "відгадування URL-адреси" наявність унікального ідентифікатора (послідовного чи іншого) не означає піддавати цей ідентифікатор користувачам програми.
Дейв Шерохман

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

60

Автокредитні ключі мають переважно переваги.

Але можливими недоліками можуть бути:

  • Якщо у вас є бізнес-ключ, вам також потрібно додати унікальний індекс у цьому стовпчику, щоб застосувати правила ведення бізнесу.
  • При передачі даних між двома базами даних, особливо коли дані знаходяться в більш ніж одній таблиці (тобто головна / детальна інформація), це не є прямолінійним, оскільки послідовності не синхронізуються між базами даних, і вам доведеться створити таблицю еквівалентності спочатку за допомогою діловий ключ як збіг, щоб дізнатися, який ідентифікатор із бази даних початків відповідає якому ідентифікатору в цільовій базі даних. Однак це не повинно бути проблемою при передачі даних з / до ізольованих таблиць.
  • На багатьох підприємствах є спеціальні інструменти для графічного звітування, графіки, зведення та перетягування. Оскільки автоінкрементальні ідентифікатори безглузді, цьому типу користувачів буде важко зрозуміти дані поза "додатком".
  • Якщо ви випадково змінили бізнес-ключ, велика ймовірність, що ви ніколи не відновите цей рядок, оскільки у вас більше немає нічого, щоб люди могли його ідентифікувати. Це викликало помилку в платформі BitCoin один раз .
  • Деякі дизайнери додають ідентифікатор до таблиці з'єднання між двома таблицями, коли ПК просто повинен складатися з двох іноземних ідентифікаторів. Очевидно, якщо таблиця приєднання знаходиться між трьома або більше таблицями, то автоматичний ідентифікаційний ідентифікатор має сенс, але тоді вам потрібно додати унікальний ключ, коли він застосовується до комбінації FK, щоб застосовувати бізнес-правила.

Ось розділ статті Вікіпедії про недоліки сурогатних ключів.


13
Звинувачувати недоліки mt.gox на сурогатних клавішах здається досить сумнівно. Проблема полягала в тому, що вони включали всі поля у складений ключ, навіть поля, що змінюються / змінюються.
CodesInChaos

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

4
Ще один недолік полягає в тому, що якщо система зростає настільки великою, що вам доведеться шарувати базу даних, ви більше не можете використовувати автоматичне збільшення для створення глобально унікального ключа. Коли ви дістанетесь до цього моменту, у вас може бути багато коду, спираючись на це припущення. Існують й інші способи створення унікального ідентифікатора, який буде продовжувати працювати, якщо база даних буде шарована.
kasperd

1
@Voo Не гарантується, що обрана база даних підтримує це. А намагаючись реалізувати його на більш високому рівні, ніж сама база даних, це означає, що ви втрачаєте деякі гарантії, які дасть вам SQL. Нарешті, будь-яке централізоване призначення ідентифікаторів збільшить затримку, якщо у вас є розподілена система.
kasperd

1
@Voo Звичайно, незалежно від масштабу системи, не слід робити занадто багато припущень щодо природи автопосилених ідентифікаторів. Якщо у вас є лише одна база даних, вони призначаються в порядку, але немає гарантії, що вони здійснюються в порядку. І в послідовності може бути розрив, оскільки не всі угоди здійснюються.
kasperd

20

Навпаки, Ні, НЕ потрібно завжди мати числовий ПК AutoInc PK.

Якщо ви ретельно аналізуєте свої дані, ви часто виявляєте природні ключі в даних. Це часто трапляється, коли дані мають власне значення для бізнесу. Іноді ПК - це артефакти давніх систем, які ділові користувачі використовують як другу мову для опису атрибутів своєї системи. Наприклад, я бачив номери VIN транспортних засобів, які використовуються в якості основного ключа таблиці "Транспортний засіб" в системі управління автопарком.

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

Іноді ви можете використовувати ПК AutoInc для створення значущого для клієнта значення, наприклад, номерів політики. Встановлення стартового значення на щось розумне та застосування ділових правил щодо провідних нулів тощо. Це, мабуть, підхід "найкращий з обох світів".

Якщо у вас є невелика кількість значень, які є відносно статичними, використовуйте значення, які мають сенс для користувача системи. Навіщо використовувати 1,2,3, коли ви можете використовувати L, C, H, де L, H і C представляють життя, автомобіль та будинок у контексті страхування "Тип полісу" або, повертаючись до прикладу VIN, як щодо використання "TO "для Toyota? У всіх автомобілях Toyata встановлено VIN, який починається на "TO" Користувачі мають одну меншу річ, щоб пам’ятати про них, зменшує ймовірність введення помилок програмування та користувачів, а може бути навіть корисним сурогатом для повного опису в звітах управління, що робить звіти простішими. писати і, можливо, швидше створювати.

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

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

EDIT Ще один важливий випадок, коли вам не потрібен автогенерований ПК - це випадок таблиць, що представляють собою перетин двох інших таблиць. Щоб дотримуватися аналогії автомобіля, автомобіль має 0..n аксесуарів, кожен аксесуар можна знайти на багатьох автомобілях. Отже, щоб представити це, ви створюєте таблицю Car_Accessory, що містить ПК від автомобіля та аксесуарів та іншу відповідну інформацію про посилання Дати тощо.

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


4
> У всіх автомобілів Toyata є VIN, який починається "ДО" Це просто не відповідає дійсності. Вони починаються з "JT", якщо виготовлені в Японії. Американські побудовані Toyota мають абсолютно різні VINs en.wikibooks.org/wiki/…
Monty Harder

17
Don't create a second, meaningless primary key; it's wasteful and may cause errors.Однак якщо спосіб встановлення унікальності для запису - це комбінація з 6 стовпців, то приєднання до всіх 6 весь час дуже схильне до себе. Дані, природно, мають ПК, але вам краще використовувати idстовпчик і унікальне обмеження для цих 6 стовпців.
Бред

14
Зізнаюсь, деякі з цих пропозицій для мене трохи віддаляються. Так, бути прагматичним це нормально, але я не можу порахувати, наскільки часто хтось клявся в житті свого первістка, що якийсь атрибут із домену залишатиметься унікальним протягом решти днів. Ну, зазвичай, це спрацювало добре до другого тижня після виходу наживо, коли з’явилися перші дублікати. ;) Використовувати "опис" як ПК дуже далеко.
AnoE

2
@Monty, мій поганий, ти маєш рацію. Помилкова пам’ять, минуло 20 років, як я сконструював системи управління флотом. Ні, VIN не був первинним ключем :) Я використав AutoInc Asset_ID IIRC, що призводить до того, що я забув. Таблиці, які є сполучниками для багато-багато-багато відносин, де ви посилаєтесь, скажімо, автомобіль на аксесуар (наприклад, люк) Багато автомобілів мають багато аксесуарів, тому вам потрібна таблиця "Car_Accessory", яка містить Car_ID та Accessory_ID, але абсолютно НЕ потрібна Car_Accesory_ID як ПК автоматичного включення.
mcottle

7
Це справді дивовижно, як мало ТРІБНО незмінних «природних ключів». SSN? Ні, вони можуть змінитися. Це рідко, але може статися. Імена користувачів? Ні. Врешті-решт у когось з’явиться справжня ділова причина для зміни. VIN часто є прикладом підручника, але їх не так багато. Навіть домашні адреси можуть змінюватися, враховуючи зміни в назві вулиць.
Ерік Функенбуш

12

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

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


3
Чи не слід використовувати "природні" ідентифікатори лише в якості первинних ключів, якщо вони абсолютно гарантовано ніколи не змінюються? Наприклад, не слід використовувати номер посвідчення водія в якості основного ключа, тому що якщо людина отримає нове водійське посвідчення, вам потрібно буде оновити не тільки цю таблицю, але й будь-які таблиці із зовнішніми ключами, на які посилається!
ekolis

1
Є кілька причин, через які номер посвідчення водія не визнається природним унікальним ідентифікатором. По-перше, деякі з них походять з інших даних, наприклад, дати народження та імені. Вони не гарантуються унікальними для всіх держав. І якщо взяти ваш приклад, коли людині переоформляють ліцензію з тим же номером, але, можливо, продовженим терміном дії, що буде потім? Вони мають різну ліцензію з однаковим номером. Натуральний ідентифікатор все ще повинен відповідати основним властивостям первинного ключа. Номер водійського посвідчення (принаймні в США) має деякі недоліки в цьому плані.
Бред Томас

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

10

У великих системах ID - це прискорювач узгодженості, використовуйте його майже де завгодно. У цьому контексті окремі первинні ключі НЕ рекомендуються, вони є дорогими в нижньому рядку (читайте чому).

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

Тут багато відповідей свідчать про те, що слід взяти існуючий унікальний ключ. Ну навіть якщо він має 150 символів? Я не думаю, що так.

Тепер моя основна думка:

Схоже, що опоненти цілочисельного ідентифікатора для самоврядування говорять про невеликі бази даних, що містять до 20 таблиць. Там вони можуть дозволити собі індивідуальний підхід до кожного столу.

АЛЕ, коли у вас є ERP з 400+ таблицями, цілий ідентифікатор автоматичного збільшення в будь-якому місці (крім випадків, зазначених вище) просто має великий сенс. Ви не покладаєтесь на інші унікальні поля, навіть якщо вони є і забезпечені для унікальності.

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

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


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

8

Не зайва практика зайвих конструкцій. Тобто - це недобра практика завжди мати автоматичний приріст int первинного ключа, коли той не потрібен.

Давайте подивимось приклад, коли він не потрібен.

У вас є таблиця статей - в ній є первинний ключ int idта стовпчик varchar з назвою title.

У вас також є таблиця з категоріями статей - idint первинний ключ, varchar name.

В одному рядку таблиці "Статті" є id5 і title "Як приготувати гусака з маслом". Ви хочете пов’язати цю статтю з такими рядками у вашій таблиці категорій: "Птиця" ( id : 20), "Гусак" ( id : 12), "Готування" ( id : 2), "Масло" (id: 9) .

Тепер у вас є 2 таблиці: статті та категорії. Як ви створюєте стосунки між ними?

У вас може бути таблиця з 3 стовпцями: id (первинний ключ), article_id (зовнішній ключ), категорія_id (зовнішній ключ). Але тепер у вас є щось на кшталт:

| id | a_id | c_id |
| 1 | 5 | 20 |
| 2 | 5 | 12 |
| 3 | 5 | 2 |

Краще рішення - мати первинний ключ, який складається з 2 стовпців.

| a_id | c_id |
| 5 | 20 |
| 5 | 12 |
| 5 | 2 |

Це можна досягти, зробивши:

create table articles_categories (
  article_id bigint,
  category_id bigint,
  primary key (article_id, category_id)
) engine=InnoDB;

Ще одна причина не використовувати ціле число автоматичного збільшення - це якщо ви використовуєте UUID для свого основного ключа.

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

Зрештою, вам не слід думати про первинні ключі як про річ. Вам потрібно думати про них як про функцію, яку вони виконують. Для чого потрібні первинні ключі? Щоб мати можливість однозначно ідентифікувати конкретні набори даних із таблиці, використовуючи поле, яке не буде змінено в майбутньому. Вам потрібен певний стовпець, покликаний idце зробити, або ви можете базувати цю унікальну ідентифікацію на інших (незмінних) даних?


7

Або є сценарії, коли ви не хочете додавати таке поле?

Звичайно.

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

Що ще важливіше, подумайте про те, що насправді є ідентифікатором - це первинний ключ для ваших даних. Якщо у вас є таблиця з іншим первинним ключем, тоді вам не потрібен ідентифікатор, і він не повинен мати. Наприклад, таблиця (EMPLOYEE_ID, TEAM_ID)(де кожен працівник може одночасно перебувати в декількох командах) має чітко визначений первинний ключ, що складається з цих двох ідентифікаторів. Додавання IDстовпця з автоматичним збільшенням , яке також є первинним ключем для цієї таблиці, взагалі не мало б сенсу. Тепер ви пересуваєте 2 первинні ключі навколо, і перше слово "первинний ключ" повинно дати вам підказку, що у вас дійсно має бути лише один.


9
(Не користувач Oracle так пробачить питання, але) чи Oracle не використовує послідовність так само, як інші використовують автопосилення / ідентичність? Це означає, що Oracle не має типу даних про автоматичне збільшення насправді лише сематичним аргументом?
Бред

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

немає двох первинних ключів, є лише один первинний ключ, і всі інші називаються ключами-кандидатами, якщо вони теж можуть слугувати первинними ключами ..
rahul tiagi

7

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

Я можу придумати кілька ситуацій, коли ви не хочете їх використовувати, більшість з яких зводиться до сценаріїв, коли одна таблиця в одному екземплярі БД не може бути авторитетним джерелом для нових значень ідентифікатора:

  • Коли додаткові посвідчення особи будуть надто великою кількістю інформації для потенційного зловмисника. Використання стовпчика особистих даних для служб передачі даних, що стоять перед громадськістю, робить вас вразливим до "проблеми німецьких танків"; якщо ідентифікатор запису 10234 існує, очевидно, що записи 10233, 10232 тощо існують назад, принаймні до запису 10001, і тоді легко перевірити записи 1001, 101 та 1, щоб зрозуміти, з чого починався ваш стовпець особи. GU4 V4, складені в основному випадковими даними, порушують таку інкрементальну поведінку за дизайном, так що лише тому, що існує один GUID, GUID, створений при збільшенні або зменшенні байту GUID, не обов'язково існує, що ускладнює використання зловмисника службою, яка відзначається для одноразового пошуку як дамп-інструменту. Є й інші заходи безпеки, які можуть краще обмежити доступ, але це допомагає.
  • У таблицях перехресних посилань M: M. Це така собі хитрість, але я бачив це, як це робилося раніше. Якщо у вашій базі даних є багатозахисні стосунки між двома таблицями, рішення "перехід до" - це перехресна таблиця, що містить стовпці із зовнішніми ключами, що посилаються на ПК кожної таблиці. ПК цієї таблиці практично завжди повинен бути складовим ключем двох зовнішніх ключів, щоб отримати вбудовану поведінку індексу та забезпечити унікальність посилань.
  • Коли ви плануєте багато чого вставляти та видаляти в цю таблицю. Напевно, найбільшим недоліком стовпців ідентичності є додаткова луна, яку вам доведеться пройти, роблячи вставку рядків з іншої таблиці або запиту, де ви хочете зберегти ключові значення оригінальної таблиці. Вам слід увімкнути "вставку особистості" (однак це зроблено у вашій СУБД), потім вручну переконатися, що ключі, які ви вставляєте, є унікальними, а потім, коли ви закінчите з імпортом, потрібно встановити лічильник ідентифікаторів у метадані таблиці до максимального присутнього значення. Якщо в цій таблиці багато трапляється, розгляньте іншу схему ПК.
  • Для розподілених таблиць.Стовпці ідентичності відмінно підходять для баз даних одного примірника, невдалої пари та інших сценаріїв, коли один екземпляр бази даних є єдиним авторитетом для всієї схеми даних у будь-який момент часу. Однак є лише настільки великий, на який ви можете піти, і все ж один комп’ютер має бути досить швидким. Реплікація або доставка журналу транзакцій може отримати додаткові копії лише для читання, але також існує обмеження щодо масштабу цього рішення. Рано чи пізно вам знадобляться два або більше екземплярів сервера, які обробляють вставки даних і потім синхронізують один з одним. Коли ця ситуація настає, вам потрібно буде використовувати GUID-поле замість додаткового, оскільки більшість СУБД заздалегідь налаштовані для використання частини GUID-кодів, які вони генерують як ідентифікатор конкретного примірника, а потім генерують решту ідентифікатора або випадковим чином або поступово В будь-якому випадку,
  • Коли вам доведеться надати унікальність для кількох таблиць у БД.Наприклад, у системах бухгалтерського обліку, як правило, можна керувати Генеральною книгою (рядок для кожного кредиту чи дебету кожного рахунку, який коли-небудь траплявся, тому він стає дуже великим дуже швидко) як послідовність таблиць, кожна з яких представляє один календарний місяць / рік. Потім можна створити представлення, щоб з'єднати їх для звітування. Логічно, це все дуже велика таблиця, але її подрібнення полегшує завдання з обслуговування БД. Однак у ньому представлена ​​проблема, як керувати вставками в декілька таблиць (дозволяючи розпочати реєстрацію транзакцій у наступному місяці, все ще закриваючи останній), не закінчуючи дублюючими ключами. Знову ж таки, GUID замість цілих цілих стовпців ідентичності є вирішенням, оскільки СУБД призначена для генерування їх по-справжньому унікальним способом,

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


1
Є випадки, коли вам все-таки знадобиться ідентифікатор у таблицях M: N (використовуючи стовпці ID, ID_M, ID_N) через приєднання властивостей до примірників вашого відношення M: N.
miroxlav

Інструкції V4 не гарантують використання криптографічно сильного PNRG, тому ви дійсно не повинні покладатися на нього на своєму першому прикладі imo (хоча, якщо ваш db-двигун дає більш сильні обіцянки, ви, можливо, все добре, але це досить не портативно). Інакше добре аргументований пост.
Voo

1
@miroxlav - я б запевнив, що якщо в таблиці достатньо додаткових метаданих щодо взаємозв'язку, що окремий ПК за межами двох ФК є гарною ідеєю, це вже насправді не є таблицею перехресних посилань; це власне ціле, що трапляється посилатися на двох інших.
KeithS

@Voo - Ви маєте рацію, GUID з V4 не гарантовано є криптографічно випадковими, просто унікальними (як і всі GUID). Однак кількість хвостів американських реактивних винищувачів також не генерується з криптографічно випадкових даних / алгоритмів насіння. Те, що ви насправді шукаєте, - це малонаселений домен; GUID V4 має 112 байт випадкових даних, здатних однозначно ідентифікувати записи 5e33.
KeithS

Для того, щоб поставити цю цифру в перспективі, кожен чоловік, жінка і дитина на планеті (усі 7 мільярдів) могли б мати 741 трлн індивідуально каталогізованих та ідентифікованих точок даних у нашій БД, і ми все ще використовували б лише одне значення GUID на мільярд наявного. Big Data, як глобальна галузь, навіть не близький до такої шкали знань. Навіть заданий шаблон для генерації GUID, існують й інші джерела ентропії, наприклад, порядок введення даних у систему та присвоєний GUID.
KeithS

7

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

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

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

Ще одне питання - це тип даних ключа, що автоматично збільшується. Використання Int32 дає великий, але відносно обмежений діапазон значень. Особисто я часто використовую стовпці bigint для Id, щоб практично ніколи не турбуватися про втрату значень.


6

Оскільки інші люди зробили справу щодо збільшення первинного ключа, я зроблю його для GUID:

  • Це гарантовано унікальне
  • Ви можете здійснити одну меншу поїздку до бази даних для отримання даних у вашій програмі. (Наприклад, у таблиці типів ви можете зберігати GUID у додатку та використовувати його для отримання запису. Якщо ви використовуєте ідентифікатор, вам потрібно запитувати базу даних по імені, і я бачив багато програм, які це роблять, щоб отримати ПК і пізніше запитує його знову, щоб отримати повну інформацію).
  • Це корисно для приховування даних. www.domain.com/Article/2 Дає мені знати, що у вас є лише дві статті, тоді як www.domain.com/article/b08a91c5-67fc-449f-8a50-ffdf2403444a мені нічого не говорить.
  • Ви можете легко об'єднати записи з різних баз даних.
  • MSFT використовує GUIDE для ідентичності.

Редагування: Дублікат точки


5
-1. GUID / UUID не гарантується, що він унікальний, і не є на 100% унікальним. GUID все ще має обмежену довжину, тому в якийсь момент ви можете ризикувати отримати дублікат, хоча це малоймовірно. Ваша думка щодо меншої кількості поїздок до бази даних також недійсна - чому ви не можете зберігати основний ідентифікатор у програмі, як це можливо за допомогою ключа GUID?
Niklas H

2
Джефф Етвуд каже, що це набагато краще, ніж я коли-небудь міг. blog.codinghorror.com/primary-keys-ids-versus-guids
Логіка трьох значень

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

Ви нічого не говорили про створення ідентифікаторів у додатку - ви просто написали "зберігання". Але якщо необхідно створити ідентифікатор за межами бази даних, то так, GUID може бути відповіддю.
Niklas H

2
Я б додав, що вони краще масштабуються. Великі бази даних NoSQL на зразок Cassandra навіть не підтримують ключі автоматичного збільшення.
Карл Білефельдт

2

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

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

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

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


2

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

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

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

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

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

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

Спеціальні справи

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

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

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


-1

Мало того, що це не є хорошою практикою, адже вона описується як анти-візерунок у книзі SQL Antipatterns Білла Карвіна.

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


це , здається, не пропонує нічого істотного над точкою зроблена і пояснена в раніше 9 відповідей
комар

2
і чому це може бути важливим?
гнат

3
@gnat Тому що це книга про кращі практики, яка безпосередньо стосується питання. Хіба це не очевидно?
Педро Вернек

3
не найменший. Пошук у Google за «найкращими методами книги sql» показує близько 900 000 посилань на мене, чому б це було особливо гідне
gnat

1
@gnat Я не збираюся сперечатися цілий день. Вам не подобається відповідь, саме для цього потрібні події.
Педро Вернек

-2

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

Зазвичай я роблю вказівники більш очевидними іменами полів на зразок ref_{table}чи подібною ідеєю.

Якщо зовнішньо не потрібно вказувати на запис, вам не потрібен ідентифікатор.


Ключове значення перекидання?
AJJ

Непідписане ціле число має максимальне значення 4294967295, перш ніж додати 1, переверне його на 0. Пам'ятайте, якщо ви додасте запис, то видаліть його, лічильник все одно збільшується. Переконайтеся, що ви використовуєте unsigned intдля типу поля, інакше обмеження становить половину цього числа.
Джонні V

Переповнення Integer - en.wikipedia.org/wiki/Integer_overflow
Джонні V

2
Якщо ви додасте / вилучіть багато рядків, лічильник автоматичного збільшення збільшиться з часом.
Джонні V

1
Як люди обробляють перекидання? Що робити, якщо є записи з низьким ідентифікатором, які ніколи не видаляються, але ви починаєте до кінця, де деякі ідентифікатори знаходяться у верхньому кінці 4294967295? Чи можна здійснити "переіндексацію"?
AJJ

-2

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


-3

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

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

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

Звичайно, не всі бази даних підтримують це. У такому випадку я зазвичай використовую таблицю, в якій зберігаються ключові відра та використовуються великі та низькі діапазони, якими керує додаток. Це найефективніше рішення, яке я знаходжу, тому що ви отримуєте діапазон 10000 номерів і автоматично збільшуєте їх у екземплярі програми. Інший екземпляр програми може забрати ще одне відро з числами для роботи. Вам потрібен достатньо великий примітив первинного ключа, наприклад, 64-бітний.

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


3
Ні ключі автоматичного збільшення означає, що збільшення ключа здійснюється автоматично базою даних. Іноді (я дивлюся на тебе, Oracle!) Для цього потрібна комбінація послідовності + тригерів, але ніколи не потрібно шукати введене раніше значення для ключа, додати 1, а потім використовувати його.
SQB

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