Стовпець NVARCHAR як PRIMARY KEY або як UNIQUE column


11

Я розробляю базу даних SQL Server 2012 і маю сумніви щодо стовпців nvarchar як первинних ключів.

У мене є ця таблиця:

CREATE TABLE [dbo].[CODES]
(
    [ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [FLAG] [tinyint] NOT NULL,
    [IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
     CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE_LEVEL] ASC,
        [CODE] ASC
    )
)

Але тепер я хочу використовувати [CODE]стовпчик як основний ключ і видалити [ID_CODE]стовпець.

Чи є питання або штраф, якщо у мене NVARCHARстовпець як PRIMARY KEY?

[CODE]Значення стовпця повинно бути унікальним, тому я вважав, що можу встановити UNIQUEобмеження для цього стовпця.

Чи потрібно використовувати [CODE]як основний ключ, чи краще, якщо я встановлюю UNIQUEобмеження на [CODE]стовпчик?


1
Досить важливою справою є те, скільки рядків буде у вашій таблиці?
James Z

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

@Manngo, дякую за ваш коментар Так, я зробив так: ID_CODE є первинним ключем, а CODE - УНІКАЛЬНИМ.
VansFannel

Відповіді:


13

Так, абсолютно є негативні наслідки для використання рядка замість числового типу для Первинного ключа, і тим більше, якщо цей ПК є кластеризованим (що це дійсно у вашому випадку). Однак ступінь, на який ви бачите ефект (и) використання рядкового поля, є функцією: a) скільки рядків у цій таблиці, і b) скільки рядків в інших таблицях є іноземними клавішами до цього ПК. Якщо у вас є лише 10k рядків у цій таблиці та 100k рядків у кількох інших таблицях, які переходять у цю таблицю через це поле, то, можливо, це буде не так помітно. Але ці ефекти, безумовно, стають помітнішими в міру збільшення кількості рядків.

Вам потрібно врахувати, що поля в кластерному індексі переносяться на некластеризовані індекси. Таким чином, ви не просто переглядаєте до 40 байт на рядок, але (40 * деякий чисел) байт. І в будь-яких таблицях FK у вас є ті самі 40 байтів у рядку плюс частіше, ніж не буде кластерного індексу на цьому полі, як воно використовується в JOINs, тому тепер він дійсно подвоюється в будь-яких таблицях, до яких FK ось цей. Якщо хтось схильний думати, що 40 байт * 1 мільйон рядків * 10 примірників цього нічого не турбує, дивіться мою статтю Диск дешевий! ОРЛІ? яка детально описує всі (або принаймні більшість) областей, на які впливає це рішення.

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

Отже, використовувати щось на кшталт CHAR(5), ймовірно, буде добре для кластеризованого ПК, але в основному, якби це також було визначено COLLATE Latin1_General_100_BIN2(або щось подібне).

І чи може [CODE]колись змінюватися значення? Якщо так, то це ще більше причин не використовувати його як ПК (навіть якщо ви встановите значення FK ON UPDATE CASCADE). Якщо він не може або ніколи не змінить, це добре, але все ж є більш ніж достатньо причин, щоб не використовувати його як кластерну ПК.

Звичайно, питання може бути неправильно сформульоване, оскільки, схоже, у вас в цьому ПК вже є це поле.

Незалежно від того , ваш кращий варіант, безумовно, полягає в використанні в [ID_CODE]якості кластерного ПК, використовувати це поле у відповідних таблицях як FK, і зберегти [CODE]як UNIQUE INDEX(це означає , що це «альтернативний ключ»).


Оновіть
трохи більше інформації на основі цього питання в коментарі до цієї відповіді:

Чи найкращим варіантом [ID_CODE], як ПЕРВІЙНИЙ КЛЮЧ, є використання стовпця [CODE] для пошуку таблиці?

Все це залежить від безлічі факторів, деякі з яких я вже згадував, але перезавантажую:

Первинний ключ - це те, як ідентифікується окремий рядок, незалежно від того, на нього посилаються будь-які іноземні ключі. Як ваша система внутрішньо ідентифікує рядок, пов'язана, але не обов'язково така сама, як, як ваші користувачі ідентифікують себе / цей рядок. Будь-який стовпець NOT NULL з унікальними даними може працювати, але є питання щодо практичності, особливо якщо ПК фактично посилається на будь-які FK. Наприклад, GUID є унікальними, і деякі люди дуже люблять використовувати їх з різних причин, але вони досить погані для кластерних індексів ( NEWSEQUENTIALIDкраще, але не ідеально). З іншого боку, GUID-файли є просто відмінними клавішами, які використовує додаток для пошуку рядка, але ПРИЄДНАННЯ все ще робиться за допомогою INT (або подібного) ПК.

Поки ви не говорили нам, як [CODE]поле вписується в систему з усіх кутів, поза тим, як згадати, що саме так ви шукаєте рядки, але чи це для всіх запитів чи лише деяких? Звідси:

  • Щодо [CODE]значення:

    • Як воно генерується?
    • Це додаткові чи псуедо-випадкові?
    • Це рівномірна чи різна довжина?
    • Які символи використовуються?
    • Якщо ви використовуєте алфавітні символи: це чутливість до регістру чи нечутливість?
    • Чи може він колись змінитися після вставки?
  • Щодо цієї таблиці:

    • Чи є інші таблиці FK до цієї таблиці? Або ці поля ( [CODE]або [ID_CODE]) використовуються в інших таблицях, навіть якщо явно не є іноземними ключами?
    • Якщо [CODE] єдине поле використовується для отримання окремих рядків, то для якої мети це [ID_CODE]поле? Якщо він не використовується, навіщо це в першу чергу (що може залежати від відповіді "Чи може [CODE]поле колись змінюватися?")?
    • Скільки рядків у цій таблиці?
    • Якщо інші таблиці посилаються на цю таблицю, то скільки і скільки рядків у кожній з них?
    • Які індекси для цієї таблиці?

Це рішення не може бути прийнято виключно з питання "NVARCHAR так чи ні?". Я ще раз скажу, що загалом кажучи, я не вважаю це гарною ідеєю, але, безумовно, є випадки, коли це добре. Враховуючи так мало полів у цій таблиці, мало ймовірно, що є ще якісь, або принаймні не багато, індекси. Таким чином, вам може бути добре в будь-якому випадку мати [CODE]як індекс кластера. І якщо жодна інша таблиця не посилається на цю таблицю, то, можливо, ви також будете добре, зробивши її ПК. Але якщо інші таблиці посилаються на цю таблицю, то я б вибрав [ID_CODE]поле як ПК, навіть якщо не кластеризовано.


Чи хотів би анонімний прихильник (який, здається, також відповідав за відповідь @noIDonthissystem), запропонувати будь-яку конструктивну критику або вказати на якусь хибну логіку?
Соломон Руцький

Дякую за вашу відповідь. Чи [ID_CODE], як PRIMARY KEY, найкращий варіант, якщо я використовую [CODE]стовпець для пошуку таблиці?
VansFannel

@VansFannel, будь ласка, дивіться моє оновлення. Дякую.
Соломон Руцький

Я приєднався до цієї спільноти dba, щоб просто підтримати цю відповідь.
Ахмет Арслан

6

Ви повинні відокремити поняття:

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

  • кластерний індекс - це концепція зберігання , фізична властивість. Це повинен бути найпоширеніший шлях доступу для запитів, він повинен слугувати для задоволення індексу покриття для більшості випадків та задоволення якомога більшої кількості діапазонів запитів.

Не потрібно, щоб основним ключем був кластерний індекс. Ви можете мати ID_CODEяк ПК, так і (CODE_LEVEL, CODE)кластерний ключ. Або навпаки.

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

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

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

Це , так би мовити, мій 2с: NVARCHAR(20)це НЕ широкий. Це абсолютно прийнятний кластерний розмір клавіш, навіть для великого столу.


Дякую за вашу відповідь. Чи [ID_CODE], як PRIMARY KEY, найкращий варіант, якщо я використовую [CODE]стовпець (а можливо [CODE_LEVEL]) для пошуку таблиці?
VansFannel

@VansFannel тільки ви можете відповісти на це.
Рем Русану

Але, на вашу думку ...
VansFannel

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

Дякую. Я буду використовувати [CODE]стовпчик як ПЕРШИЙ КЛЮЧ.
VansFannel

4

Я ніколи не дозволяв би нікому зробити nvarchar(20)ПК в моїй базі даних. Ви витрачаєте дисковий простір і кеш-пам'ять. Кожен індекс цієї таблиці та всі ФК у ній повторюють це широке значення. Можливо, знаряддя (20), якщо вони можуть це виправдати. Які дані ви намагаєтесь зберігати CODE? Вам справді потрібно зберігати символи nvarchar? Я прагну робити ПК "внутрішніми" значеннями, які не бачать користувачі, і я намагаюся зберігати значення, які відображаються окремо. Відображені значення іноді потребують зміни, що стає дуже проблематичним для ПК + FK.

Крім того, чи усвідомлюєте ви, що "ідентифікація bigint (1,1)" може збільшуватися до 9,223,372,036,854,775,807?

[ID_CODE] [bigint] IDENTITY(1,1)

Якщо ви не будуєте цю базу даних для Google, не буде достатньо нормальної норми int identity (1,1)з її обмеженням понад 2 мільярди?


int - 4 байти в SQL, що дає від -2,1 млрд до + 2,1 млрд.
datagod

@datagod, га дякую, стільки цифр я неправильно порахував!
немає ідентифікатора в цій системі

Дякую за вашу відповідь. Чи [ID_CODE], як PRIMARY KEY, найкращий варіант, якщо я використовую [CODE]стовпець для пошуку таблиці? Дякую.
VansFannel

Я був у цьому човні, поки мені хтось не користувався послідовним характером "int" для передбачення даних / користувачів у моїй БД та збирав більшість всього, що мав. Ніколи знову. Громадському, що стикається з БД, потрібно отримати трохи складніше для отримання інформації.
DaBlue

3

Не повинно бути ніякого притаманного / помітного штрафу, окрім як ви ризикуєте використовувати широкі клавіші під час використання nvarchar / varchar, якщо цього не знаєте. Особливо, якщо ви почнете комбінувати їх у складених клавішах.

Але у вашому прикладі довжини (20) у вас має бути добре, і я б не переймався цим. Тому що якщо CODE - це, як ви в основному запитуєте свої дані - кластерний індекс на це звучить дуже розумно.

Однак вам слід розглянути, чи дійсно ви хочете його в якості основного ключа або просто унікального (кластерного) індексу. Існує (невелика) різниця між кластеризованим індексом та первинним ключем (в основному - первинний ключ ідентифікує ваші дані, але індекс - це спосіб запиту даних), тому, якщо ви хочете, ви можете так само легко зробити свій ID_Code як первинний ключ і зробити унікальний кластерний індекс над CODE. (зауважте: SQL Server автоматично перетворить ваш первинний ключ у кластерний індекс, якщо ви вручну не створили кластерний індекс самостійно)

Також врахуйте, чи дійсно вам потрібен ID_Code, тепер у вас є унікальний КОД.


2
Насправді розміром NVARCHAR(20)є 40 байт (макс.), І оскільки це стовпчик змінної довжини , це насправді не найкращий вибір для кластерного індексу. ID_CODEБути a BIGINT IDENTITYбуде набагато кращим вибором тут!
marc_s

Я знаю, що це 40 байтів, але не було багато причин визначати це, бачачи, що це ніде близько 900 байт. І якщо ви в основному запитуєте дані з CODE, це буде кращим вибором, щоб уникнути зайвих індексів для підтримки, тому що вам все одно знадобиться індекс на ньому, і тоді вам доведеться шукати через кластеризовані вперед
Аллан С. Хансен

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