私は最近ROWGUIDの概念を研究しており、この質問に出くわしました。 この答えは洞察を与えましたが、主キーの値を変更するという言及で、私を別のウサギの穴に導きました。
私の理解では、主キーは不変である必要があり、この回答を読んだ結果、ベストプラクティスと同じものを反映した回答しか得られなかったため、検索を続けてきました。
レコードが作成された後に主キー値を変更する必要があるのはどのような状況ですか?
私は最近ROWGUIDの概念を研究しており、この質問に出くわしました。 この答えは洞察を与えましたが、主キーの値を変更するという言及で、私を別のウサギの穴に導きました。
私の理解では、主キーは不変である必要があり、この回答を読んだ結果、ベストプラクティスと同じものを反映した回答しか得られなかったため、検索を続けてきました。
レコードが作成された後に主キー値を変更する必要があるのはどのような状況ですか?
回答:
人の名前を主キーとして使用していて、その名前を変更した場合は、主キーを変更する必要があります。これは、主キーとの外部キー関係を持つすべての関連テーブルに変更をカスケードする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文字であると考えると、大量のメモリを節約できます。
キーがどのように、なぜ変更できるかについての質問に対する接線は、サロゲートキーよりも自然キーを選択する理由についての質問です。これは、特にパフォーマンスが設計目標である場合、興味深い、おそらくより重要な質問です。私の質問を参照してくださいここではそれについて。
私の経験では、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
主キーは、同期が関係するときに変更される可能性があります。これは、切断されたクライアントがあり、一定の間隔でサーバーとデータを同期する場合に該当する可能性があります。
数年前、ローカルマシン上のすべてのイベントデータに-1、-2などの負の行IDがあるシステムで作業しました。データがサーバーに同期されると、サーバーの行IDがクライアント。サーバーの次の行IDが58だったとしましょう。-1が58になり、-2が59になります。その行IDの変更は、ローカルマシン上のすべての子FKレコードにカスケードされます。このメカニズムは、以前に同期されたレコードを判別するためにも使用されました。
私はこれが良いデザインだとは言っていませんが、それは時間とともに変化する主キーの例です。
PRIMARY KEY
定期的な変更を伴う設計は、災害のレシピです。それを変更する唯一の正当な理由は、以前に別々の2つのデータベースの合併です。
@MaxVernonで指摘されているように、時折変更が発生する可能性がありますON UPDATE CASCADE
が、最近ではほとんどのシステムがIDを代理として使用していますPRIMARY KEY
。
Joe CelkoやFabian Pascal(フォローする価値のあるサイト)などの純粋主義者は、代理キーの使用に反対していますが、この特定の戦いに負けたと思います。
安定性はキーにとって望ましいプロパティですが、相対的なものであり、絶対的なルールではありません。実際には、キーの値を変更すると便利なことがよくあります。リレーショナル用語では、データはその(スーパー)キーによってのみ識別可能です。特定のテーブルにキーが1つしかない場合、A)キー値の変更、またはB)テーブル内の行セットを他のキー値を含む類似または異なる行セットで置き換えることの違いは、本質的にロジックではなくセマンティクスの問題。
より興味深い例は、複数のキーを持つテーブルの場合で、これらのキーの1つまたは複数の値は、他のキー値に関連して変更する必要があります。LoginNameとBadge Numberという2つのキーを持つEmployeeテーブルの例を見てみましょう。そのテーブルのサンプル行を次に示します。
+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS |47832 |
+---------+--------+
ZoeSがバッジを紛失した場合、新しいバッジが割り当てられ、新しいバッジ番号を取得できます。
+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS |50282 |
+---------+--------+
後で、彼女はログイン名を変更することを決定する場合があります。
+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZSmith |50282 |
+---------+--------+
両方のキー値が変更されました-相互に関連して。「プライマリ」と見なされるものに必ずしも違いが生じるわけではないことに注意してください。
実際には、「不変性」、つまり絶対に値を変更しないことは、達成不可能であるか、少なくとも検証することが不可能です。変更によってまったく違いが生じる場合、最も安全な方法は、おそらくキー(または属性)を変更する必要があると想定することです。
興味深いことに、ROWGUIDの並べ替えに関するリンクされた質問には、独自のユースケースがあります。つまり、同期する必要があるデータベース内で競合する主キーがある場合です。調整する必要がある2つのデータベースがあり、それらが主キーのシーケンスを使用している場合、キーの1つを変更して一意性を維持する必要があります。
理想的な世界では、これは決して起こりません。最初に主キーにGUIDを使用します。ただし、現実的には、設計を開始するときに分散データベースさえ持っていない場合があり、それをGUIDに変換することは、キーの更新を実装するよりも影響が大きいと見なされたため、分散させるために以下に優先順位を付けた努力であった可能性があります。これは、整数キーに依存する大きなコードベースがあり、GUIDに変換するためにメジャーリビジョンが必要な場合に発生する可能性があります。また、スパースGUID(必要に応じてランダムに生成すると発生する、互いにあまり近くないGUID)が特定の種類のインデックスに問題を引き起こす可能性があるという事実もあります。主キー(Byron Jonesが言及)として。
誰かが国民保険番号(NIN)を主キーとして選択し、何らかの形でオペレーターが間違ったNINの行を挿入したような状況を想像してください。値を挿入した後、エラーを修正するには2つの方法があります。