主キーの値が変わるのはなぜですか?


18

私は最近ROWGUIDの概念を研究しており、この質問に出くわしました。 この答えは洞察を与えましたが、主キーの値を変更するという言及で、私を別のウサギの穴に導きました。

私の理解では、主キーは不変である必要があり、この回答を読んだ結果、ベストプラクティスと同じものを反映した回答しか得られなかったため、検索を続けてきました。

レコードが作成された後に主キー値を変更する必要があるのはどのような状況ですか?


7
不変ではない主キーが選択された場合
ypercubeᵀᴹ

2
これまでのところ、以下のすべての答えに少しだけ注意を払ってください。主キーがクラスター化インデックスでもある場合を除き、主キーの値を変更することはそれほど重要ではありません。クラスタ化インデックスの値が変更された場合にのみ本当に重要です。
ケネスフィッシャー

6
@KennethFisherまたは、他のテーブルまたは同じテーブルの1つ(または多数)のFKによって参照され、多くの(おそらく数百万または数十億)行に変更をカスケードする必要がある場合。
ypercubeᵀᴹ

9
Skypeに聞いてください。数年前にサインアップしたときに、ユーザー名を間違って入力しました(姓から文字を残しました)。私は何度も修正しようとしましたが、主キーに使用され、変更をサポートしていないため、変更できませんでした。これは、顧客が主キーの変更を望んでいるインスタンスですが、Skypeはそれをサポートしていませんでした。彼ら望むならその変更をサポートすることができます(または、より良いデザインを作成することができます)が、現在それを許可する場所は何もありません。だから私のユーザー名はまだ間違っています。
アーロンバートランド

3
実際の値はすべて(さまざまな原因で)変化する可能性があります。これは、代理キー/合成キーの元々の動機の1つでした。絶対に変更することなく信頼できる人工的な値を生成できるようにすることです。
–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;

完全を期すために、更新ステートメントの計画は非常に単純であり、サロゲートキーには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上記の2つのステートメントの出力は次のとおりです。

ここに画像の説明を入力してください

基本的に、結果はほぼ同じです。大きな違いの1つは、外部キーが発生するすべてのテーブルで広い自然キーが繰り返されないことです。この例では、VARCHAR(200)列を使用して人の名前を保持しているため、VARCHAR(200) everywhereを使用する必要があります。多数の行と外部キーを含む多数のテーブルがある場合、それは多くの無駄なメモリになります。ほとんどの人がディスクスペースは本質的に無料であるほど安いと言っているので、ディスクスペースが無駄になっていることについては話していないことに注意してください。ただし、メモリは高価であり、大事に値します。キーに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を変更する必要がある場合は、関連する行をNULLに更新できます(チャンク、多数の場合、またはテーブルごとに) 、多数のテーブルがある場合、または両方)、PK値を変更してから、FKを再度変更します。
アーロンバートランド

8

私の経験では、PKとして自然および/または可変のキーを使用できますが、これらの条件を満たすPKを使用することで防止できることが多い問題につながります。

 Guaranteed Unique, Always Exists, Immutable, and Concise.

たとえば、米国の多くの企業は、システムで社会保障番号を個人ID番号(およびPK)として使用しようとしています。その後、次の問題が発生します。複数のレコードを修復する必要があるデータ入力エラー、SSNを持たない人々、政府によってSSNが変更された人々、SSNが重複している人々。

私はそれらのシナリオを一つ一つ見てきました。また、顧客を「単なる数字」にしたくない企業を見てきました。つまり、PKが「first + middle + last + DOB + zip」または同様のナンセンスになりました。一意性をほぼ保証するのに十分なフィールドを追加しましたが、クエリは恐ろしく、これらのフィールドのいずれかを更新することは、データの一貫性の問題を追いかけることを意味しました。

私の経験では、データベース自体によって生成されたPKはほとんどの場合、より良いソリューションです。

追加のポインターについては、この記事をお勧めします:http : //www.agiledata.org/essays/keys.html


6
あなたの答えで言及されているスコット・アンブラーの記事からの良いアドバイス:「自然の鍵を常に使用すべきだと言う人もいれば、常に代理鍵を使用すべきだと言う人もいます。これらの人々は常に間違っていることがわかります。彼らは「データ宗教」の偏見を共有する以上のことはしていません。現実には、自然キーと代理キーにはそれぞれ長所と短所があり、すべての状況に最適な戦略はありません。
nvogel

7

主キーは、同期が関係するときに変更される可能性があります。これは、切断されたクライアントがあり、一定の間隔でサーバーとデータを同期する場合に該当する可能性があります。

数年前、ローカルマシン上のすべてのイベントデータに-1、-2などの負の行IDがあるシステムで作業しました。データがサーバーに同期されると、サーバーの行IDがクライアント。サーバーの次の行IDが58だったとしましょう。-1が58になり、-2が59になります。その行IDの変更は、ローカルマシン上のすべての子FKレコードにカスケードされます。このメカニズムは、以前に同期されたレコードを判別するためにも使用されました。

私はこれが良いデザインだとは言っていませんが、それは時間とともに変化する主キーの例です。


5

PRIMARY KEY定期的な変更を伴う設計は、災害のレシピです。それを変更する唯一の正当な理由は、以前に別々の2つのデータベースの合併です。

@MaxVernonで指摘されているように、時折変更が発生する可能性がありますON UPDATE CASCADEが、最近ではほとんどのシステムがIDを代理として使用していますPRIMARY KEY

Joe CelkoFabian Pascalフォローする価値のあるサイト)などの純粋主義者は、代理キーの使用に反対していますが、この特定の戦いに負けたと思います。


3

安定性はキーにとって望ましいプロパティですが、相対的なものであり、絶対的なルールではありません。実際には、キーの値を変更すると便利なことがよくあります。リレーショナル用語では、データはその(スーパー)キーによってのみ識別可能です。特定のテーブルにキーが1つしかない場合、A)キー値の変更、またはB)テーブル内の行セットを他のキー値を含む類似または異なる行セットで置き換えることの違いは、本質的にロジックではなくセマンティクスの問題。

より興味深い例は、複数のキーを持つテーブルの場合で、これらのキーの1つまたは複数の値は、他のキー値に関連して変更する必要があります。LoginNameとBadge Numberという2つのキーを持つEmployeeテーブルの例を見てみましょう。そのテーブルのサンプル行を次に示します。

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

ZoeSがバッジを紛失した場合、新しいバッジが割り当てられ、新しいバッジ番号を取得できます。

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

後で、彼女はログイン名を変更することを決定する場合があります。

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

両方のキー値が変更されました-相互に関連して。「プライマリ」と見なされるものに必ずしも違いが生じるわけではないことに注意してください。

実際には、「不変性」、つまり絶対に値を変更しないことは、達成不可能であるか、少なくとも検証することが不可能です。変更によってまったく違いが生じる場合、最も安全な方法は、おそらくキー(または属性)を変更する必要があると想定することです。


「実際には「不変性」、すなわち絶対に値を変更しないことは、達成不可能であるか、少なくとも検証することが不可能である」という声明のために、私はあなたのコメントを否定しました。不変性は可能ですが、サロゲートキーを使用する最も重要な理由の1つです。
バイロンジョーンズ

3
来週または10年後に誰かがキー値を変更しないことをどのようにして知ることができますか?あなたは彼らがそうしないと思うかもしれませんが、あなたはそれが起こるのを現実的に防ぐことはできません(あなたが唯一の責任者であるなら、他の人を永久に締め出すために障壁を立てることができますが、それはエッジケースのようです)。本当に重要なのは、変更が非常にまれであり、決して発生しないということではありません。
nvogel

3

興味深いことに、ROWGUIDの並べ替えに関するリンクされた質問には、独自のユースケースがあります。つまり、同期する必要があるデータベース内で競合する主キーがある場合です。調整する必要がある2つのデータベースがあり、それらが主キーのシーケンスを使用している場合、キーの1つを変更して一意性を維持する必要があります。

理想的な世界では、これは決して起こりません。最初に主キーにGUIDを使用します。ただし、現実的には、設計を開始するときに分散データベースさえ持っていない場合があり、それをGUIDに変換することは、キーの更新を実装するよりも影響が大きいと見なされたため、分散させるために以下に優先順位を付けた努力であった可能性があります。これは、整数キーに依存する大きなコードベースがあり、GUIDに変換するためにメジャーリビジョンが必要な場合に発生する可能性があります。また、スパースGUID(必要に応じてランダムに生成すると発生する、互いにあまり近くないGUID)が特定の種類のインデックスに問題を引き起こす可能性があるという事実もあります。主キー(Byron Jonesが言及)として。


0

考えられるシナリオの1つとして、一意のIDを持つアフィリエイトがあり、アフィリエイトが一意の開始文字を持っているため、アフィリエイト間で重複しないことがわかっているとします。アフィリエイトは、マスターテーブルにデータをロードします。レコードが処理され、マスターIDが割り当てられます。ユーザーは、まだ処理されていない場合でも、ロードされるとすぐにレコードにアクセスする必要があります。マスターIDは処理された順序に基づいており、レコードがロードされた順序で常に処理するとは限りません。私は少し製造されていることを知っています。


-1

誰かが国民保険番号(NIN)を主キーとして選択し、何らかの形でオペレーターが間違ったNINの行を挿入したような状況を想像してください。値を挿入した後、エラーを修正するには2つの方法があります。

  1. 誤ったレコードを削除して、新しいレコードを挿入します
  2. 値を正しい値に更新し、その列に参照整合性制約がある場合は更新時カスケードを使用します
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.