私は最近ROWGUIDの概念を調査していて、 this の質問に出くわしました。 これ 答えは洞察を与えましたが、主キーの値を変更することについて言及して、別のうさぎの穴を掘り下げました。
私の理解では、主キーは不変である必要があり、この回答を読んでからの検索では、ベストプラクティスと同じことを反映する回答しか提供されていません。
レコードの作成後に主キー値を変更する必要があるのはどのような場合ですか?
人の名前を主キーとして使用していて、その名前が変更された場合、主キーを変更する必要があります。これは、基本的にカスケード主キーとの外部キー関係を持つすべての関連テーブルへの変更のため、_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;
_
上記の2つのSELECT
ステートメントの出力は次のとおりです。
基本的に、結果はほぼ同じです。主な違いの1つは、外部キーが発生するすべてのテーブルでワイド自然キーが繰り返されないことです。私の例では、VARCHAR(200)
列を使用して人の名前を保持しています。これには、VARCHAR(200)
everywhereを使用する必要があります。多くの行と外部キーを含む多くのテーブルがある場合、メモリの浪費につながります。ほとんどの人はディスクスペースが本質的に無料であるほど安いと言っているので、私はディスクスペースが無駄になることについて話しているのではないことに注意してください。ただし、メモリは高価であり、大切にしておく価値があります。キーに4バイト整数を使用すると、名前の平均の長さを約15文字と考えると、大量のメモリを節約できます。
接線どのようにとなぜキーを変更できるかについての質問は、なぜ自然を選択するかについての質問ですこれは、特にパフォーマンスが設計目標である場合、興味深い、おそらくより重要な質問です。それについて私の質問- ここ を見てください。
あなたのPKとして自然で可変のキーを使用することはできますが、私の経験では、これらの条件を満たすPKを使用することで問題を防ぐことができます。
Guaranteed Unique, Always Exists, Immutable, and Concise.
たとえば、米国の多くの企業は、システムで社会保障番号を個人ID番号(およびPK)として使用しようとしています。次に、次の問題が発生します-修復が必要な複数のレコードにつながるデータ入力エラー、SSNを持たない人々、政府によってSSNが変更された人々、重複したSSNを持っている人々。
私はそれらのシナリオをひとつひとつ見てきました。また、顧客を「単なる数字」にしたくない会社も見ました。つまり、PKが「最初+中間+最後+ DOB +郵便番号」または同様のナンセンスになってしまったということです。一意性をほぼ保証するのに十分なフィールドを追加しましたが、クエリは恐ろしいものでした。これらのフィールドのいずれかを更新すると、データの整合性の問題を追跡する必要がありました。
私の経験では、ほとんどの場合、データベース自体によって生成された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 (フォローする価値のあるサイト)などの純粋主義者は、代理キーの使用に同意しませんが、この特定の戦いに負けたと思います。
興味深いことに、ROWGUIDについてのリンクされた質問は、独自のユースケースを提供します。つまり、同期する必要のあるデータベースに競合する主キーがある場合です。調整が必要なデータベースが2つあり、それらが主キーのシーケンスを使用している場合は、キーの1つを変更して、一意性を保つことができます。
理想的な世界では、これは決して起こらないでしょう。まず、主キーにGUIDを使用します。ただし、現実的には、設計を開始するときに分散データベースを使用していないこともあり、GUIDに変換することは、キーの更新を実装するよりも影響が大きいと考えられたため、分散化することを優先して行われた可能性があります。これは、整数キーに依存する大規模なコードベースがあり、GUIDに変換するためにメジャーリビジョンが必要な場合に発生する可能性があります。まばらなGUID(相互に非常に接近していないGUID、必要に応じてランダムに生成した場合に発生します)が特定の種類のインデックスで問題を引き起こす可能性があるという事実もあります。つまり、使用を避けたいということです。それらを主キーとして( Byron Jones で言及)。
安定性はキーにとって望ましい特性ですが、それは相対的なものであり、絶対的な規則ではありません。実際には、キーの値を変更すると便利なことがよくあります。リレーショナル用語では、データはその(スーパー)キーによってのみ識別可能です。したがって、特定のテーブルにキーが1つしかない場合、A)キー値の変更、またはB)テーブル内の行のセットを他のキー値を含む類似または異なる行のセットで置き換えることは、本質的に区別されます。論理ではなく意味論の問題。
より興味深い例は、複数のキーを持つテーブルの場合で、それらのキーの1つ以上の値が他のキー値との関係で変化しなければならない場合があります。 LoginNameとBadge Numberの2つのキーを持つEmployeeテーブルの例を見てみましょう。そのテーブルのサンプル行は次のとおりです。
+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS |47832 |
+---------+--------+
ZoeSがバッジを紛失した場合、おそらく新しいバッジが割り当てられ、新しいバッジ番号を取得します。
+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZoeS |50282 |
+---------+--------+
後で、彼女は自分のログイン名を変更することに決めるかもしれません:
+---------+--------+
|LoginName|BadgeNum|
+---------+--------+
|ZSmith |50282 |
+---------+--------+
相互に関連して、両方のキー値が変更されました。ただし、「プライマリ」と見なされるものは必ずしも違いはありません。
実際には、「不変性」、つまり絶対に値を変更しないことは、達成不可能であるか、少なくとも確認することが不可能です。変更によって少しでも違いが生じる場合は、おそらく最も安全な方法は、キー(または属性)を変更する必要があると想定することです。
考えられる1つのシナリオは、一意のIDを持つアフィリエイトがあり、一意の開始文字があるため、アフィリエイト間で重複しないことを知っているとします。アフィリエイトはマスターテーブルにデータを読み込みます。レコードが処理され、マスターIDが割り当てられます。ユーザーは、まだ処理されていなくても、ロードされるとすぐにレコードにアクセスする必要があります。マスターIDは、処理された注文に基づいたものにする必要があり、レコードがロードされた順に処理するとは限りません。私は少し捏造を知っています。