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

18

Я недавно исследовал концепцию ROWGUID и наткнулся на этот вопрос. Этот ответ дал понимание, но привел меня в другую кроличью нору с упоминанием об изменении значения первичного ключа.

Я всегда понимал, что первичный ключ должен быть неизменным, и мой поиск после прочтения этого ответа дал только ответы, которые отражают то же, что и передовой опыт.

При каких обстоятельствах значение первичного ключа необходимо изменить после создания записи?

5crammed
источник
7
Когда выбран первичный ключ, который не является неизменным?
ypercubeᵀᴹ
2
На данный момент просто небольшая гнида ко всем ответам ниже. Изменение значения в первичном ключе не так уж сложно, если только первичный ключ не является кластерным индексом. Это действительно имеет значение только в случае изменения значений кластерного индекса.
Кеннет Фишер
6
@KennethFisher или если на него ссылаются один (или несколько) FK в другой или той же таблице, и изменение должно быть каскадно на множество (возможно, миллионы или миллиарды) строк.
ypercubeᵀᴹ
9
Спроси скайп. Когда я зарегистрировался несколько лет назад, я неправильно набрал свое имя пользователя (пропустил письмо из моей фамилии). Я много раз пытался исправить это, но они не могли изменить его, потому что он использовался для первичного ключа, и они не поддерживали его изменение. Это тот случай, когда клиент хочет, чтобы первичный ключ был изменен, но 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;

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

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

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 обнуляемыми, поэтому, если вам нужно изменить PK, вы можете обновить соответствующие строки до NULL (в кусках, если их много, или по таблице , если есть много таблиц или и то, и другое), а затем измените значение PK, а затем снова измените FK.
Аарон Бертран
8

Хотя вы можете использовать ключ, который является естественным и / или изменяемым, в качестве вашего PK, по моему опыту, это приводит к проблемам, которые часто можно предотвратить с помощью PK, который удовлетворяет следующим условиям:

 Guaranteed Unique, Always Exists, Immutable, and Concise.

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

Я видел каждый из этих сценариев. Я также видел компании, которые не хотели, чтобы их клиенты были «просто числом», что означало, что их PK в конечном итоге был «первый + средний + последний + DOB + почтовый индекс» или что-то подобное. Несмотря на то, что они добавили достаточно полей, чтобы практически гарантировать уникальность, их запросы были ужасными, и обновление любого из этих полей означало поиск проблем с согласованностью данных.

По моему опыту, PK, созданный самой базой данных, почти всегда является лучшим решением.

Я рекомендую эту статью для дополнительных указателей: http://www.agiledata.org/essays/keys.html

Байрон Джонс
источник
6
Один хороший совет из статьи Скотта Амблера, на которую вы ссылаетесь в вашем ответе: «Некоторые люди скажут вам, что вы всегда должны использовать естественные ключи, а другие скажут вам, что вы всегда должны использовать суррогатные ключи. Эти люди неизменно оказываются неправы, обычно они делают немного больше, чем делятся с вами предрассудками своей «религии данных». Реальность такова, что у естественных и суррогатных ключей есть свои преимущества и недостатки, и ни одна стратегия не идеальна для всех ситуаций ».
nvogel
7

Первичный ключ может быть изменен при синхронизации. Это может быть в том случае, если у вас отключен клиент, и он через определенные промежутки времени синхронизирует данные с сервером.

Несколько лет назад я работал в системе, где все данные о событиях на локальном компьютере имели отрицательные идентификаторы строк, такие как -1, -2 и т. Д. Когда данные были синхронизированы с сервером, идентификатор строки на сервере был применен к клиент. Допустим, следующий идентификатор строки на сервере был 58. Тогда -1 станет 58, -2 59 и так далее. Это изменение идентификатора строки будет каскадно добавлено ко всем дочерним записям FK на локальном компьютере. Этот механизм также использовался для определения того, какие записи были ранее синхронизированы.

Я не говорю, что это был хороший дизайн, но это пример изменения первичного ключа с течением времени.

Джон Рейнор
источник
5

Любой дизайн, который предполагает изменение PRIMARY KEYна регулярной основе, является рецептом для катастрофы. Единственной хорошей причиной для его изменения будет объединение двух ранее отдельных баз данных.

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

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

Verace
источник
3

Стабильность является желательным свойством для ключа, но это относительная вещь, а не абсолютное правило. На практике часто бывает полезно изменить значения ключей. В реляционных терминах данные можно идентифицировать только по их (супер) ключам. Отсюда следует, что если в данной таблице имеется только один ключ, то различие между A) изменением значения ключа или B) заменой набора строк в таблице некоторым аналогичным или другим набором строк, содержащих другие значения ключа, по существу проблема семантики, а не логики.

Более интересным примером является случай таблицы с несколькими ключами, где значения одного или нескольких из этих ключей могут измениться относительно других значений ключа. Возьмите пример таблицы Employee с двумя ключами: LoginName и Badge Number. Вот пример строки из этой таблицы:

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

Если ZoeS потеряет свой значок, то, возможно, ей назначат новый значок и получат новый номер значка:

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

Позже она может решить изменить свое имя пользователя:

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

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

На практике «неизменность», т. Е. Абсолютно никогда не меняющее значение, недостижима или, по крайней мере, невозможна для проверки. В той степени, в которой это изменение имеет значение вообще, самый безопасный курс, вероятно, предполагает, что любой ключ (или любой атрибут) может потребоваться изменить.

nvogel
источник
Я отклонил ваш комментарий из-за следующего утверждения: «На практике« неизменность », то есть абсолютно никогда не меняющая значение, недостижима или, по крайней мере, невозможна для проверки». Неизменность возможна и является одной из наиболее важных причин использования суррогатных ключей.
Байрон Джонс
3
Как вы можете знать, что кто-то не изменит значение ключа на следующей неделе или через 10 лет? Вы можете предположить, что они этого не сделают, но вы не можете реально предотвратить это когда-либо (если вы несете единоличную ответственность, вы могли бы установить барьеры, чтобы удержать всех остальных в вечности, я полагаю, но это похоже на крайний случай). Что действительно важно, так это то, что изменения происходят очень редко, а не то, что они никогда не произойдут.
nvogel
3

Интересно, что связанный вопрос о типе ROWGUID предоставляет свой вариант использования: когда у вас есть конфликтующие первичные ключи в базах данных, которые необходимо синхронизировать. Если у вас есть две базы данных, которые необходимо согласовать, и они используют последовательности для первичных ключей, вам нужно изменить один из ключей, чтобы он мог оставаться уникальным.

В идеальном мире этого никогда бы не случилось. Для начала вы бы использовали GUID для первичных ключей. Реально, однако, у вас может даже не быть распределенной базы данных, когда вы начнете проектировать, и преобразование ее в GUID могло быть попыткой, которая была расставлена ​​по приоритетам ниже, чтобы сделать ее распределенной, потому что это считалось более значительным, чем реализация обновления ключа. Это может произойти, если у вас большая кодовая база, которая зависит от целочисленных ключей и потребует серьезной ревизии для преобразования в GUID. Существует также тот факт, что разреженные идентификаторы GUID (идентификаторы GUID, которые не очень близки друг к другу, что происходит, если вы генерируете их случайным образом, как вы должны), могут также вызывать проблемы для определенных типов индексов, что означает, что вы хотите избежать использования их как первичные ключи (упомянутые Байроном Джонсом ).

jpmc26
источник
0

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

папараццо
источник
-1

Представьте себе ситуацию, когда кто-то выбрал номер социального страхования (NIN) в качестве первичного ключа и оператор каким-то образом вставил строку с неправильным NIN. После вставки значения есть два способа исправить ошибку:

  1. Удалите ошибочную запись и вставьте новую
  2. Обновите значение до правильного и используйте Каскад обновления, если в этом столбце есть ограничение ссылочной целостности.
Бехруз Самены
источник