Чому змінилося б значення первинного ключа?


18

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

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

За яких обставин потрібно змінити значення первинного ключа після створення запису?


7
Коли вибирається первинний ключ, який не змінюється?
ypercubeᵀᴹ

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

6
@KennethFisher або якщо на нього посилається один (або багато) ФК в іншій або одній і тій же таблиці, і зміни повинні бути каскадними до багатьох (можливо, мільйонів чи мільярдів) рядків.
ypercubeᵀᴹ

9
Запитайте Skype. Коли я зареєструвався кілька років тому, я неправильно ввів своє ім’я користувача (залишив лист із мого прізвища). Я багато разів намагався виправити його, але вони не змогли його змінити, оскільки він використовувався для основного ключа, і вони не підтримували його зміну. Це такий випадок, коли клієнт хоче, щоб основний ключ змінився, але Skype не стався, щоб це підтримати. Вони могли б підтримати цю зміну, якби хотіли (або могли б створити кращий дизайн), але наразі немає нічого, що дозволило б це зробити. Тому моє ім’я все ще неправильне.
Аарон Бертран

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

Відповіді:


24

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

Наприклад:

USE tempdb;
GO

CREATE TABLE dbo.People
(
    PersonKey VARCHAR(200) NOT NULL
        CONSTRAINT PK_People
        PRIMARY KEY CLUSTERED
    , BirthDate DATE NULL
) ON [PRIMARY];

CREATE TABLE dbo.PeopleAKA
(
    PersonAKAKey VARCHAR(200) NOT NULL
        CONSTRAINT PK_PeopleAKA
        PRIMARY KEY CLUSTERED
    , PersonKey VARCHAR(200) NOT NULL
        CONSTRAINT FK_PeopleAKA_People
        FOREIGN KEY REFERENCES dbo.People(PersonKey)
        ON UPDATE CASCADE
) ON [PRIMARY];

INSERT INTO dbo.People(PersonKey, BirthDate)
VALUES ('Joe Black', '1776-01-01');

INSERT INTO dbo.PeopleAKA(PersonAKAKey, PersonKey)
VALUES ('Death', 'Joe Black');

А SELECTпроти обох таблиць:

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonKey = pa.PersonKey;

Повернення:

введіть тут опис зображення

Якщо ми оновимо PersonKeyстовпець і повторно запустимо SELECT:

UPDATE dbo.People
SET PersonKey = 'Mr Joe Black'
WHERE PersonKey = 'Joe Black';

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonKey = pa.PersonKey;

ми бачимо:

введіть тут опис зображення

Дивлячись на план вищесказаного UPDATE твердження, ми чітко бачимо, що обидві таблиці оновлюються одним твердженням оновлення в силу зовнішнього ключа, визначеного як ON UPDATE CASCADE:

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

Нарешті, ми очистимо наші тимчасові таблиці:

DROP TABLE dbo.PeopleAKA;
DROP TABLE dbo.People;

Кращий 1 способом зробити це за допомогою сурогатних ключів буде:

USE tempdb;
GO

CREATE TABLE dbo.People
(
    PersonID INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_People
        PRIMARY KEY CLUSTERED
    , PersonName VARCHAR(200) NOT NULL
    , BirthDate DATE NULL
) ON [PRIMARY];

CREATE TABLE dbo.PeopleAKA
(
    PersonAKAID INT NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_PeopleAKA
        PRIMARY KEY CLUSTERED
    , PersonAKAName VARCHAR(200) NOT NULL
    , PersonID INT NOT NULL
        CONSTRAINT FK_PeopleAKA_People
        FOREIGN KEY REFERENCES dbo.People(PersonID)
        ON UPDATE CASCADE
) ON [PRIMARY];

INSERT INTO dbo.People(PersonName, BirthDate)
VALUES ('Joe Black', '1776-01-01');

INSERT INTO dbo.PeopleAKA(PersonID, PersonAKAName)
VALUES (1, 'Death');

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonID = pa.PersonID;

UPDATE dbo.People
SET PersonName = 'Mr Joe Black'
WHERE PersonID = 1;

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

введіть тут опис зображення

SELECT *
FROM dbo.People p
    INNER JOIN dbo.PeopleAKA pa ON p.PersonID = pa.PersonID;

DROP TABLE dbo.PeopleAKA;
DROP TABLE dbo.People;

Вихід із двох SELECTвищезазначених тверджень:

введіть тут опис зображення

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

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


1 - http://weblogs.sqlteam.com/mladenp/archive/2009/10/06/Why-I-prefer-surrogate-keys-instead-of-natural-keys-in.aspx


3
Щоб уникнути CASCADE (який має проблеми в певних сценаріях), ви також можете зробити стовпці FK нульовими, тому, якщо вам потрібно змінити ПК, ви можете оновити відповідні рядки на NULL (в шматках, якщо їх багато, або за таблицею , якщо таблиць багато, або обох), а потім змініть значення PK, а потім знову змініть FK.
Аарон Бертран

8

Хоча ви можете використовувати ключ, який є природним та / або зміненим у якості ПК, на мій досвід, це призводить до проблем, які часто можна запобігти використанням ПК, який відповідає цим умовам:

 Guaranteed Unique, Always Exists, Immutable, and Concise.

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

Я бачив кожен із цих сценаріїв. Я також бачив компанії, які не хотіли, щоб їхні клієнти були "просто числом", а це означало, що їх ПК виявився "перший + середній + останній + DOB + zip" або якась подібна дурниця. Хоча вони додавали достатньо полів, щоб майже гарантувати унікальність, їх запити були жахливими, і оновлення будь-якого з цих полів означало відмову від проблем узгодженості даних.

На мій досвід, ПК, створений самою базою даних, майже завжди є кращим рішенням.

Я рекомендую цю статтю для додаткових покажчиків: http://www.agiledata.org/essays/keys.html


6
Один хороший рада з статті Скотта Амблера, на який посилається у вашій відповіді: "Деякі люди скажуть вам, що ви завжди повинні користуватися природними ключами, а інші вам скажуть, що ви завжди повинні використовувати сурогатні ключі. Ці люди незмінно виявляються неправими, як правило. вони роблять трохи більше, ніж ділитися з вами забобонами своєї "релігії даних". Реальність полягає в тому, що природні та сурогатні ключі мають свої переваги та недоліки, і що жодна стратегія не ідеальна для всіх ситуацій ".
nvogel

7

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

Кілька років тому я працював у системі, де всі дані про події на локальній машині мали від’ємні ідентифікатори рядків, наприклад -1, -2 тощо. Коли дані синхронізувались із сервером, ідентифікатор рядка на сервері застосовувався до клієнт. Скажімо, наступний рядок Id на сервері становив 58. Тоді -1 стане 58, -2 59 тощо. Ця зміна ідентифікатора рядка буде застосовано до всіх дочірніх записів FK на локальній машині. Механізм також використовувався для визначення того, які записи раніше синхронізовані.

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


5

Будь-який дизайн, який передбачає PRIMARY KEYрегулярну зміну, - це рецепт катастрофи. Єдиною вагомою причиною її зміни було б об'єднання двох раніше окремих баз даних.

Як зазначає @MaxVernon, можуть виникати випадкові зміни - тоді використовуйте ON UPDATE CASCADE, хоча більшість систем на сьогоднішній день використовують ідентифікатор як сурогат PRIMARY KEY.

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


3

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

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

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS     |47832   |
+---------+--------+

Якщо ZoeS втратить свій значок, можливо, їй буде призначений новий і отримає новий номер значка:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS     |50282   |
+---------+--------+

Пізніше вона може вирішити змінити своє ім’я для входу:

+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZSmith   |50282   |
+---------+--------+

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

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


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

3
Як ви можете знати, що хтось не змінить ключового значення на наступному тижні чи через 10 років? Ви можете припустити, що вони не стануть, але ви не можете реально запобігти тому, щоб це коли-небудь відбувалося (якщо ви несете єдину відповідальність, то ви можете поставити бар'єри, щоб уникнути всіх інших у вічності, але, напевно, це здається кращим випадком). Що насправді важливо, це те, що зміни дуже рідкісні, а не те, що вони ніколи не відбудуться.
nvogel

3

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

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


0

Один з можливих сценаріїв - скажімо, у вас є афілійовані особи, які мають унікальний ідентифікатор, і ви знаєте, що вони не будуть дублюватися між філіями, оскільки вони мають унікальний початковий характер. Філії завантажують дані в основну таблицю. Там записи обробляються, а потім присвоюється основний ідентифікатор. Користувачі потребують доступу до записів, як тільки вони завантажуються, навіть якщо вони ще не оброблені. Ви хочете, щоб основний ідентифікатор базувався на оброблюваному замовленні, і ви не завжди будете обробляти в тому порядку, коли були завантажені записи. Я знаю трохи сфабрикований.


-1

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

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