サロゲート vs.ナチュラルキー/ビジネスキー [終了]
-
09-06-2019 - |
質問
さあ、また行きましょう、昔ながらの議論がまだ起きています...
ビジネス キーを主キーとして使用する方がよいでしょうか、それともサロゲート ID を使用する方がよいでしょうか (つまり、SQL Server ID) にビジネス キー フィールドの一意の制約があるか?
あなたの理論を裏付ける例や証拠を提供してください。
解決
両方。ケーキを持って食べてください。
主キーには、そのようにラベルが付けられていることを除いて、特別なことは何もないことに注意してください。これは単なる NOT NULL UNIQUE 制約であり、テーブルには複数の制約を含めることができます。
代理キーを使用する場合でも、ビジネス ルールに従って一意性を確保するためにビジネス キーが必要です。
他のヒント
代理キーを使用する理由は次のとおりです。
安定性:ビジネス上または自然な必要性のためにキーを変更すると、関連テーブルに悪影響が生じます。値には意味が関連付けられていないため、代理キーを変更する必要があることは、たとえあったとしてもほとんどありません。
大会:PK にさまざまな名前を付けてテーブルを結合する方法を考える必要がなく、標準化された主キー列の命名規則を使用できるようになります。
スピード:PK の値とタイプによっては、整数の代理キーの方が小さく、インデックス付けと検索が高速になる場合があります。
非代理キー (「自然」と言うのは躊躇します) キーを支持する発言はまだ誰もしていないようです。それで、ここに行きます...
あ 不利益 代理キーの特徴は、 意味のない (利点として挙げる人もいますが...)。これにより、実際に必要な数よりも多くのテーブルをクエリに結合する必要が生じることがあります。比較する:
select sum(t.hours)
from timesheets t
where t.dept_code = 'HR'
and t.status = 'VALID'
and t.project_code = 'MYPROJECT'
and t.task = 'BUILD';
に対して:
select sum(t.hours)
from timesheets t
join departents d on d.dept_id = t.dept_id
join timesheet_statuses s on s.status_id = t.status_id
join projects p on p.project_id = t.project_id
join tasks k on k.task_id = t.task_id
where d.dept_code = 'HR'
and s.status = 'VALID'
and p.project_code = 'MYPROJECT'
and k.task_code = 'BUILD';
誰かが次のことが良いアイデアだと真剣に考えていない限り?:
select sum(t.hours)
from timesheets t
where t.dept_id = 34394
and t.status_id = 89
and t.project_id = 1253
and t.task_id = 77;
「しかし、MYPROJECTやVALIDやHRのコードが変更されたらどうなるのか」と誰かが言うでしょう。 私の答えは次のようになります。「なぜそうするのですか 必要 外部機関が今後「VALID」を「GOOD」として再コード化するよう法的に制定するという意味では、これらは「自然な」キーではありません。実際にそのカテゴリに分類される「自然」キーはほんのわずかです。SSN や郵便番号が一般的な例です。私なら間違いなく、人物、住所などのテーブルには意味のない数値キーを使用しますが、 すべて, 、何らかの理由で、ここにいるほとんどの人がこれを支持しているようです。
以下も参照してください。 別の質問に対する私の答え
サロゲート キー (通常は整数) には、テーブル リレーションを高速化し、ストレージと更新速度をより経済的にするという付加価値があります (さらに良いことに、ビジネス キー フィールドとは対照的に、サロゲート キーを使用する場合は外部キーを更新する必要がありません)。それは時々変わります)。
テーブルの主キーは、主に結合の目的で、行を一意に識別するために使用する必要があります。Persons テーブルを考えてみましょう。名前は変更される可能性があり、一意であることは保証されません。
企業について考えてみましょう:あなたは Merkia の他の企業と取引を行っている幸せな Merkin 会社です。あなたは会社名を主キーとして使用しないほど賢いので、10 文字の英数字全体で Merkia 政府の一意の会社 ID を使用します。その後、Mercia は良いアイデアだと考えて会社 ID を変更します。大丈夫、そもそもあなたが関与すべきではない変更のために、データベース エンジンのカスケード更新機能を使用しています。その後、あなたのビジネスは拡大し、現在はフリードニアの会社と協力しています。フリードニアの企業 ID は最大 16 文字です。会社 ID の主キー (Orders、Issues、MoneyTransfers などの外部キー フィールドも) を拡大し、主キー (外部キーにも) に Country フィールドを追加する必要があります。ああ!フリードニア内戦、三国に分裂。アソシエイトの国名を新しい国名に変更する必要があります。カスケードアップデートが助けになります。ところで、主キーは何ですか?(国、会社 ID) または (会社 ID、国)?後者は結合に役立ち、前者は別のインデックス (注文を国別にグループ化したい場合はおそらく多数) を回避します。
これらはすべて証拠ではありませんが、結合操作を含むあらゆる用途で行を一意に識別する代理キーの方がビジネス キーよりも好ましいことを示しています。
サロゲートキーを変更する理由は決してありません。自然キーについては同じことは言えません。姓、メールアドレス、ISBN 番号はすべて、いつか変わる可能性があります。
私は代理キーが大嫌いです。これらは、高品質の自然キーが利用できない場合にのみ使用してください。考えてみると、意味のないデータをテーブルに追加することで状況が改善されると考えるのは、かなりばかげています。
私の理由は次のとおりです。
自然キーを使用すると、最も頻繁に検索される方法でテーブルがクラスタ化されるため、クエリが高速になります。
サロゲート キーを使用する場合は、論理キー列に一意のインデックスを追加する必要があります。論理的な重複データを防ぐ必要があります。たとえば、pk がサロゲート ID 列であっても、組織テーブル内で同じ名前の 2 つの組織を許可することはできません。
代理キーが主キーとして使用される場合、自然主キーが何であるかはあまり明確になりません。開発時には、どの列のセットがテーブルを一意にするかを知りたいと思います。
1 対多の関係チェーンでは、論理キー チェーン。たとえば、組織には多数のアカウントがあり、アカウントには多数の請求書があります。したがって、Organization の論理キーは OrgName です。Accounts の論理キーは OrgName、AccountID です。Invoice の論理キーは OrgName、AccountID、InvoiceNumber です。
代理キーが使用される場合、キー チェーンは直接の親への外部キーのみを持つことによって切り詰められます。たとえば、Invoice テーブルには OrgName 列がありません。AccountID の列のみがあります。特定の組織の請求書を検索する場合は、組織、アカウント、および請求書のテーブルに参加する必要があります。論理キーを使用する場合は、組織テーブルに直接クエリを実行できます。
ルックアップ テーブルの代理キー値を保存すると、テーブルが無意味な整数で埋められるようになります。データを表示するには、すべてのルックアップ テーブルに結合する複雑なビューを作成する必要があります。ルックアップ テーブルは、列に許容される値のセットを保持することを目的としています。代わりに整数の代理キーを保存することによってコード化するべきではありません。正規化ルールには、値自体の代わりにサロゲート整数を保存する必要があることを示唆するものはありません。
私は 3 つの異なるデータベースの本を持っています。どれも代理キーの使用を示していません。
この終わりのない戦争に関する私の経験を皆さんと共有したいと思います:D 自然キーと代理キーのジレンマについて。私はそう思います 両方 代理キー (人工的に自動生成されたもの) と自然キー (ドメインの意味を持つ列で構成される) には、 長所 そして 短所. 。したがって、状況に応じて、どちらかの方法を選択する方が適切な場合があります。
多くの人が代理キーをほぼ完璧な解決策として提示し、自然キーを疫病として提示しているようですので、私は別の観点の議論に焦点を当てます。
代理キーの欠点
代理キーは次のとおりです。
- パフォーマンスの問題の原因:
- これらは通常、自動インクリメントされる列を使用して実装されます。これは次のことを意味します。
- 新しい ID を取得するたびにデータベースへの往復が必要になります (キャッシュや [seq]hilo のようなアルゴリズムを使用してこれを改善できることはわかっていますが、それでもこれらの方法には独自の欠点があります)。
- ある日、あるスキーマから別のスキーマにデータを移動する必要がある場合 (少なくとも私の会社では頻繁に起こります)、ID の衝突の問題が発生する可能性があります。はい、UUID を使用できることは知っていますが、最後には 32 桁の 16 進数が必要です。(データベースのサイズを気にする場合は、それが問題になる可能性があります)。
- すべての代理キーに 1 つのシーケンスを使用している場合、確かに、データベースで競合が発生します。
- これらは通常、自動インクリメントされる列を使用して実装されます。これは次のことを意味します。
- エラーを起こしやすい。シーケンスには max_value 制限があるため、開発者は次の点に注意する必要があります。
- シーケンスを循環させる必要があります (最大値に達すると、1、2、... に戻ります)。
- データの (時間の経過に伴う) 順序付けとしてシーケンスを使用している場合は、循環の場合に対処する必要があります (ID 1 の列は、ID max-value - 1 の行よりも新しい可能性があります)。
- コード (および内部 ID であるため発生しないはずのクライアント インターフェイスも) が、シーケンス値の格納に使用した 32b/64b 整数をサポートしていることを確認してください。
- データが重複していないことを保証するものではありません。すべて同じ列値を持つ 2 つの行を常に含めることができますが、生成された値は異なります。私にとってこれは ザ データベース設計の観点から見た代理キーの問題。
- 詳細はウィキペディアで...
自然鍵に関する神話
- 複合キーは代理キーよりも非効率的ではありません。いいえ!使用するデータベース エンジンによって異なります。
- 自然キーは現実には存在しません。申し訳ありませんが、それらは存在します。たとえば、航空業界では、次のタプルは特定の項目に関して常に一意になります。 予定されている フライト (航空会社、出発日、フライト番号、運航サフィックス)。より一般的には、ビジネス データのセットが特定の条件によって一意であることが保証されている場合、 標準 その場合、このデータ セットは [良い] 自然キーの候補になります。
- ナチュラルキーは子テーブルの「スキーマを汚染」します。私にとって、これは実際の問題というよりも感覚です。それぞれ 2 バイトの 4 列の主キーを持つ方が、11 バイトの単一列より効率的である可能性があります。さらに、4 つの列を使用して、親テーブルに結合せずに (where 句で 4 つの列を使用して) 子テーブルを直接クエリすることができます。
結論
適切な場合は自然キーを使用し、使用する方が適切な場合は代理キーを使用します。
これが誰かの役に立てば幸いです!
常にビジネス上の意味を持たないキーを使用してください。それはただ良い練習です。
編集:オンラインでリンクを見つけようとしましたが、見つかりませんでした。しかし、 「エンタープライズアーキテクチャのパターン」 [Fowler] キー以外の意味を持たないキー以外のものを使用してはならない理由がよく説明されています。つまり、ジョブは 1 つだけ、ジョブは 1 つだけ持つ必要があるということになります。
ORM ツールを使用してデータ クラスを処理/生成する予定がある場合、サロゲート キーは非常に便利です。より高度なマッパーの一部では複合キーを使用できます (次を参照)。hibernate) を使用すると、コードが若干複雑になります。
(もちろん、データベース純粋主義者は、代理キーの概念さえ忌まわしいものだと主張するでしょう。)
私は、適切な場合には代理キーに uid を使用することを好みます。彼らの大きな利点は、鍵を事前に知っていることです。すでに設定されており、一意であることが保証されている ID を使用してクラスのインスタンスを作成できますが、たとえば整数キーの場合は、デフォルトで 0 または -1 に設定し、保存/更新時に適切な値に更新する必要があります。
ただし、UID には検索速度と結合速度の点でペナルティがあるため、UID が望ましいかどうかは問題のアプリケーションによって異なります。
私の意見では、変更される可能性がゼロであるため、代理キーを使用する方が良いと考えています。自然キーとして使用できると考えられるほぼすべてのものは変更される可能性があります (免責事項:常に正しいとは限りませんが、一般的には)。
例としては、車の DB が考えられます。一見すると、ナンバー プレートがキーとして使用できると思われるかもしれません。しかし、これらは変更される可能性があるため、それは悪い考えです。本当はそれを知りたくないでしょう 後 誰かがなぜ自分のナンバープレートをピカピカの新しいパーソナライズされたものに変更できないのかを知りたいとあなたのところに来たとき、アプリをリリースします。
可能であれば、常に単一の列、代理キーを使用してください。これにより、レコードを維持するために単一の情報を追跡するだけで済むため、結合や挿入/更新/削除がより簡単になります。
次に、必要に応じて、ビジネス キーを一意の制約またはインデックスとしてスタックします。これにより、データの整合性が維持されます。
ビジネス ロジック/自然キーは変更される可能性がありますが、テーブルの物理キーは決して変更すべきではありません。
データウェアハウスのシナリオでは、サロゲート キー パスに従う方が良いと思います。2 つの理由:
- ソース システムからは独立しているため、データ型の変更など、ソース システムでの変更は影響を受けません。
- 代理キーには整数データ型のみを使用するため、DW に必要な物理スペースは少なくなります。また、インデックスの機能も向上します。
代理キーは、ビジネス情報が変更されるか、同一である可能性がある場合に役立ちます。結局のところ、企業名は全国で一意である必要はありません。Smith Electronics という名前の 2 つの企業 (1 つはカンザス州、もう 1 つはミシガン州) と取引しているとします。住所で区別できますが、状況は変わります。状態さえも変わる可能性があります。カンザス州カンザスシティのスミス エレクトロニクスが川を渡ってミズーリ州カンザスシティに移転したらどうなるでしょうか?自然キー情報を使用してこれらのビジネスを明確に区別する方法はないため、代理キーは非常に役立ちます。
代理キーは ISBN 番号のようなものだと考えてください。通常、本はタイトルと著者によって特定されます。しかし、私は H 氏の「Pearl Harbor」というタイトルの本を 2 冊持っています。P.ウィルモット、それらは版が違うだけでなく、間違いなく別の本です。このような場合、書籍の外観や、以前のものと新しいものを参照することもできますが、ISBN を頼りにするのと同じくらい良いのです。
クラスター化インデックスをランダムな代理キーに配置することは良い習慣ではないことを思い出してください。XY8D7-DFD8S を読み取る GUID。SQL Server にはこれらのデータを物理的に並べ替える機能がないためです。代わりに、これらのデータに一意のインデックスを配置する必要がありますが、メイン テーブル操作に対して単純に SQL プロファイラを実行してから、それらのデータをデータベース エンジン チューニング アドバイザに配置することも有益な場合があります。
スレッド@を参照してください http://social.msdn.microsoft.com/Forums/en-us/sqlgetstarted/thread/27bd9c77-ec31-44f1-ab7f-bd2cb13129be
ケース 1: あなたのテーブルは ルックアップテーブル 50 種類未満 (挿入)
使用 ビジネス/ナチュラルキー。例えば:
Table: JOB with 50 inserts
CODE (primary key) NAME DESCRIPTION
PRG PROGRAMMER A programmer is writing code
MNG MANAGER A manager is doing whatever
CLN CLEANER A cleaner cleans
...............
joined with
Table: PEOPLE with 100000 inserts
foreign key JOBCODE in table PEOPLE
looks at
primary key CODE in table JOB
ケース 2: あなたのテーブルは 何千もの挿入が含まれるテーブル
使用 サロゲート/自動インクリメントキー. 。例えば:
Table: ASSIGNMENT with 1000000 inserts
joined with
Table: PEOPLE with 100000 inserts
foreign key PEOPLEID in table ASSIGNMENT
looks at
primary key ID in table PEOPLE (autoincrement)
最初の場合:
- テーブル JOB との結合を使用せずに、次のコマンドを使用するだけで、テーブル PEOPLE 内のすべてのプログラマを選択できます。「ジョブコード = 'PRG' の人々から * を選択」
2 番目の場合:
- 主キーが整数であるため、データベースのクエリが高速になります。
- データベース自体が次の自動インクリメントを提供するため、次の一意のキーを見つけることに煩わされる必要はありません。
これは、代理キーが非常に重要なケースの 1 つです。 いつも 理にかなっています。データベースに最適なものを選択する場合と、オブジェクト モデルに最適なものを選択する場合がありますが、どちらの場合も、意味のないキーまたは GUID を使用することをお勧めします。これにより、インデックス作成がより簡単かつ迅速になり、オブジェクトの変更されない ID となります。
コース用の馬。私の偏見を述べますと、私はそもそも開発者なので、ユーザーに動作するアプリケーションを提供することに主に関心を持っています。
私は自然キーを使用したシステムに取り組んできましたが、値の変更が確実に反映されるようにするために多くの時間を費やす必要がありました。
私は代理キーのみを使用したシステムに取り組んできましたが、唯一の欠点は、パーティショニング用の非正規化データが欠如していることでした。
私がこれまで一緒に仕事をしてきた従来の PL/SQL 開発者のほとんどは、結合あたりのテーブル数のせいで代理キーを好まなかったのですが、私たちのテスト データベースと本番データベースでは決して問題はありませんでした。追加の結合はアプリケーションのパフォーマンスに影響を与えませんでした。「X 内部結合 Y on X.a = Y.b」などの句をサポートしていないデータベース言語、またはその構文を使用しない開発者では、代理キーの余分な結合によりクエリが読みにくくなり、入力や入力に時間がかかります。チェック:@Tony Andrews の投稿を参照してください。ただし、ORM またはその他の SQL 生成フレームワークを使用している場合は、それに気づきません。タッチタイピングも軽減されます。
このトピックとは完全に関係ないかもしれませんが、代理キーを扱うのが頭の痛い問題です。Oracle の事前配信アナリティクスは、ウェアハウス内のすべてのディメンション テーブルに自動生成された SK を作成し、それらをファクトにも保存します。そのため、新しい列が追加されたときにそれら (ディメンション) を再ロードする必要があるとき、またはディメンション内のすべての項目に値を設定する必要があるときは常に、更新中に割り当てられた SK によって、ファクトに保存されている元の値と SK が同期しなくなります。それに結合するすべてのファクト テーブルを完全にリロードします。たとえSKが意味のない数字だったとしても、オリジナル/古いレコードでは変更できない何らかの方法があることを望みます。多くの人が知っているように、そのままの状態で組織のニーズを満たすことはほとんどなく、常にカスタマイズする必要があります。現在、ウェアハウスには 3 年分のデータがあり、Oracle Financial システムからの完全なリロードは非常に大規模です。したがって、私の場合、これらはデータ入力から生成されるのではなく、レポートのパフォーマンスを支援するためにウェアハウスに追加されます。それは理解できますが、私たちの状況は変わります、それは悪夢です。
ポイントインタイムデータベースの場合は、サロゲートキーと自然キーを組み合わせて使用するのが最適です。例えばクラブの会員情報を追跡する必要があります。メンバーの一部の属性は決して変更されません。例: 生年月日ですが、名前は変更される可能性があります。したがって、member_id サロゲート キーを使用して Member テーブルを作成し、DOB の列を用意します。person name という名前の別のテーブルを作成し、member_id、member_fname、member_lname、date_updated の列を作成します。このテーブルでは、自然キーは member_id + date_updated になります。