主キーはどうですか? [閉まっている]
-
03-07-2019 - |
質問
私のチームのかなり活発な議論の中で、私はほとんどの人が主キーとして何を好むかを考えさせられました。次のグループがありました-
- Int / BigIntは、自動インクリメントで十分なプライマリキーです。
- 主キーを構成する少なくとも3つの列が必要です。
- Id、GUID、および人間が読める行識別子はすべて異なる方法で処理する必要があります。
PKにとって最適なアプローチは何ですか?あなたの意見を正当化できれば素晴らしいでしょう。上記より良いアプローチはありますか?
編集:誰でも簡単にサンプル/アルゴリズムを使用して、人間が読める形式の行の識別子を生成できますか?
解決
時々接続されるアプリを使用してデータベース間で同期を行う場合は、主キーにGUIDを使用する必要があります。デバッグには苦痛が伴うので、その場合を除き、自動インクリメントするintに固執する傾向があります。
自動インクリメントintがデフォルトであり、使用しないは正当化される必要があります。
他のヒント
本当に基本的なポイントを指摘する答えはありません(つまり、主なキーは、同じ実世界のテーブルに2つのエントリを取得しないことを保証するものです)エンティティ(データベースでモデル化されている)。この観察結果は、主キーの良い選択と悪い選択の確立に役立ちます。
たとえば、(US)州の名前とコードの表では、名前またはコードのいずれかが主キーになる可能性があります-それらは2つの異なる候補キーを構成し、そのうちの1つ(通常は短い-コード)は主キーとして選択されます。機能的依存関係(および結合依存関係-1NFから5NFまで)の理論では、主キーではなく重要なのは候補キーです。
反例として、一般的に人間の名前は主キーとして不適切な選択をします。 「John Smith」という名前で行く人はたくさんいます。または他の類似の名前。ミドルネームを考慮しても(覚えておいてください:誰もがミドルネームを持っているわけではありません-たとえば、持っていません)、複製の余地は十分にあります。その結果、人々は名前を主キーとして使用しません。社会保障番号(SSN)や従業員番号などの人工キーを発明し、それらを使用して個人を指定します。
理想的な主キーは、短く、ユニークで、記憶に残り、自然です。これらの特性のうち、一意性は必須です。残りは、実世界のデータの制約を考慮して柔軟にする必要があります。
したがって、特定のテーブルの主キーを決定する場合、そのテーブルが何を表しているのかを調べる必要があります。テーブル内の列の値のセットは、テーブル内の各行を一意に識別しますか?これらが候補キーです。ここで、各候補キーが4列または5列で構成されている場合、それらが適切なプライマリキーを作成するにはあまりにも不格好であると判断する可能性があります(主に短い理由で)。これらの状況では、代理キー(人為的に生成された番号)を導入する場合があります。多くの場合(常にではありませんが)、代理キーには単純な32ビット整数で十分です。次に、この代理キーを主キーとして指定します。
ただし、他の候補キー(サロゲートキーも候補キーであり、選択した主キー)がすべて一意の識別子として維持されるようにする必要があります これらの列セットに対する一意の制約。
行を一意にする理由を特定するのが難しい場合がありますが、情報を繰り返してもそれが真実ではないため、行を一意にする必要があります。また、注意を払わずに同じ情報を保存しようとする2つ(またはそれ以上)の行を取得し、その情報を更新する必要がある場合、1つの行のみを更新する危険性があります(特にカーソルを使用する場合)すべての行ではなく、行が同期していないため、どの行に正しい情報が含まれているかは誰にもわかりません。
これは、いくつかの点でかなりハードラインのビューです。
GUIDが必要なときに使用することに特別な問題はありませんが、それらは大きい(16-64バイトなど)である傾向があり、頻繁に使用されます。非常に多くの場合、完全に良好な4バイトの値で十分です。 4バイトの値で十分なGUIDを使用すると、ディスク領域が無駄になり、インデックスページあたりの値が少なくなるため、データへのインデックス付きアクセスでも遅くなります。そのため、インデックスを深くし、より多くのページを読み取って情報。
人々は普遍的な正しい答えを求めるため、これは宗教的な問題にすぎません。あなたのチームとこのSOスレッドの両方が非常に多くの意見の相違を示しているという事実は、あなたが説明するすべてのソリューションをさまざまな状況で使用する正当な理由があるという手がかりになるはずです。
- サロゲートキーは、テーブル内の他の属性または属性セットが行を一意に識別するのに適していない場合に役立ちます。
- 可能な場合は、テーブルを人間が読みやすくするために、自然キーが優先されます。また、ナチュラルキーを使用すると、従属テーブルの外部キーに代理IDの代わりに実際の値を含めることができます。例えば。
state
(CA、TX、NY)を保存する必要がある場合は、intではなくchar(2)
自然キーを使用することもできます。 - 必要に応じて複合主キーを使用します。 "
id
"を追加しないでください。完全に適切な複合キーが存在する場合、不必要に代理キーを使用します(これは、多対多のテーブルで特に当てはまります)。すべてのテーブルで3列のキーを使用することは、まったく無意味です。 - GUIDは、複数のサイトで一意性を維持する必要がある場合のソリューションです。主キーの値が一意である必要があるが、順序付けられていないか連続している必要がない場合にも便利です。
- INT対BIGINT:テーブルが主キーに64ビット範囲を必要とすることは一般的ではありませんが、64ビットハードウェアの可用性が向上しているため、負担になることはありません。オーバーフローしないことを保証します。 INTはもちろん小さいので、スペースが限られている場合は、わずかな利点があります。
データベースプログラマーブログこの種の情報のソースとして。
主キーの3列?ビジネスルールの要求に応じて、列には適切な一意の制約を設定する必要がありますが、別の代理キーが必要です。複合キーは、ビジネスロジックがキーに入ることを意味します。ロジックが変更されると、スキーマ全体がねじ込まれます。
私は私のユニークなものが好きです。
常にサロゲートキーを使用します。代理キー(通常はID列、自動インクリメント、またはGUID)は、キーがデータ自体に存在しないキーです。一方、自然キーは、それ自体で行を一意に識別するキーです。私が人生で知る限り、本物の自然なキーはほとんどありません。米国のSSNのようなものでさえ自然な鍵ではありません。複合主キーは、発生を待つ災害です。そのデータを編集することはできません(これは、複合キーであるかどうかにかかわらず、自然キーの主な欠点です)が、さらに悪いことに、複合キーでは、そのキーデータをすべての関連テーブルに永続化する必要があります。なんて大きな無駄。
今、サロゲートキーを選択するために、ID列を使用します(主にMS SQL Serverで作業しています)。 GUIDは大きすぎるので、MicrosoftはそれらをPKとして使用することを against お勧めします。複数のサーバーがある場合、必要なのは、10または20の増分、または同期/拡張する必要があるサーバーの最大数と考えられるものを作成し、後続の各サーバーの各テーブルのシードを含めることだけです。 、データの衝突はありません。
もちろん、増分のために、ID列をBigInt(長い[64ビット]とも呼ばれます)にします。
少しの計算を行うと、増分を100にしたとしても、テーブルに92,233,720,368,547,758(> 92兆)行を含めることができます。
「Primary」という語句は、「Primary」という語句で使用すると思います。キーは本当の意味で、紛らわしいです。
最初に、「キー」という定義を使用します;テーブル内で一意である必要がある属性または属性セットです。
その後、任意のキーを持つことは、しばしば相互に矛盾するいくつかの目的に役立ちます。
- この親テーブルと関係がある子テーブル内の1つまたは複数のレコードへの結合条件として使用します。 (これらの子テーブルで明示的または暗黙的に外部キーを定義します)
- (関連)子レコードが親タブに親レコードを持たなければならないことを保証します; e(子テーブルFKは親テーブルにキーとして存在する必要があります)
-
テーブル内の特定のレコード/行を迅速に見つける必要があるクエリのパフォーマンスを向上させるため。
-
同じ論理エンティティを表す重複行がテーブルに挿入されないようにして、データの一貫性を確保します。 (これはしばしば「自然」キーと呼ばれ、比較的不変のテーブル(エンティティ)属性で構成される必要があります。)
明らかに、意味のない完全な非自然キー(GUIDや自動生成された整数など)は、完全に#4を満たすことができません。
しかし、多くの(ほとんどの)テーブルで、#4を提供できる完全に自然なキーは、複数の属性で構成され、幅が広すぎるか、目的1、#2、または#3に使用されるほど幅が広いことがよくあります許容できないパフォーマンス結果を引き起こします。
答えは簡単です。両方を使う。他の子テーブルのすべての結合とFKに単純な自動生成整数キーを使用しますが、データの一貫性を必要とするすべてのテーブル(ごく少数のテーブルにはない)に、一貫性のないデータ行の挿入を防止する代替の固有の一意キーがあることを確認します。 ..さらに、常に両方がある場合は、自然キーを使用することに反対するすべての異議(変更するとどうなりますか。FKとして参照されるすべての場所を変更する必要があります) ..矛盾する重複データを避けるため、PKである1つのテーブルでのみ使用しています...
GUIDについては、インデックスでGUIDを使用するとインデックスの断片化が発生する可能性があるため、GUIDの使用には非常に注意してください。それらを作成するために使用される最も一般的なアルゴリズムでは、「ランダム」最上位ビット位置のGUIDの部分...これにより、新しい行が追加されると、定期的なインデックスの最適化/再インデックス付けの要件が増加します。
やや話題から外れていますが、私は...
プライマリキーがGUIDの場合、 しないでクラスター化インデックスにします。 GUIDはシーケンシャルではないため、データはほとんどすべての挿入中にディスク上に再配置されます。 (うん。)主キーとしてGUIDを使用する場合は、非クラスター化インデックスにする必要があります。
絶対にすべきでないことの1つは、スマートキーの使用です。これは、レコードに関する情報がキー自体にコード化されているキーであり、最終的には噛みつきます。
1つの場所で作業しました。主キーは、文字と数字の組み合わせであるアカウントIDでした。具体的なことは覚えていませんが、たとえば、特定のタイプのアカウントは600の範囲にあり、別のタイプのアカウントは400から始まります。仕事の種類。または、彼らがした仕事の種類を変更しました。
別の場所。ツリー内の場所をレコードの主キーとして使用します。したがって、次のようなレコードがあります。
Cat1.subcatA.record1
Cat1.subcatA.record2
Cat1.subcatB.record1
Cat2.subcatA.record1
もちろん、顧客が最初に望んだのは、ツリー内のアイテムを移動する方法でした。ソフトウェアのセット全体は、それが起こる前に死にました。
お願い、お願い、お願いです。私が保守しなければならないコードを書いているなら、スマートキーを使わないでください!
私は主キーとしての自動インクリメントのファンです。私はこれが警戒であることを心の底から知っていますが、データが追加されたとき(ORDER BY ID DESC、f'rインスタンス)にデータをソートすることをとても簡単にします。
3列は、人間が解析するのに非常に耳障りです。
それはトレードオフです-リレーショナル機能をどれだけ必要とするのか、このテーブルの右を人間がそれを尋問することを理解できるようにする(ストアドプロシージャまたはプログラムインターフェイスに対して)
自動インクリメントは私たち人間のためのものです。 :-(
一般に、それは依存します。
個人的には、自動インクリメントの整数が好きです。
しかし、私が言えることの1つは、他のソースからのデータをキーとして決して信用しないことです。私は誓った、それをするたびに、それは私に噛み付くように戻ってくる。さて、二度と!
主キーを構成する少なくとも3つの列があるはずです。
これは理解できません。
「ナチュラルキー」について話しているのですか。 「名前と生年月日」?ナチュラルキーは存在する場合に理想的ですが、ナチュラルキーのほとんどの候補は一意ではない(同じ名前の複数の人)か、一定ではありません(誰かが名前を変更できる)。
Int / BigIntは、自動インクリメントで十分なプライマリキーです。
Guidが好きです。自動インクリメントの潜在的な問題は、値(例:「注文ID」)がデータベースインスタンス(例:「販売データベース」)によって割り当てられることです...これは完全には機能しません(代わりに複合キーが必要になり始めます) )複数のデータベースインスタンスによって作成されたデータをマージする必要がある場合(たとえば、それぞれ独自のデータベースを持つ複数の営業所から)。
RE GUIDの
これが本当に本当に本当に大きなデータベース、大量の負荷、高速アクセスになるかどうかに注意してください。
1億から5億件のレコードのデータベースがあった最後の仕事で、データベース担当者はGUIDに反対し、適切なサイズの10進数を求めました。彼らは、(Oracleの下では)文字列Guidの内部ストレージのサイズの違いと10進数の値がルックアップに非常に顕著な違いをもたらすと感じました。 (より大きなキー=トラバースするより深いツリー)
GUIDのランダムな性質により、インデックスページのフィルファクターも大幅に減少します。これにより、ティアリングとディスクI / Oが劇的に増加します。
列を自動インクリメントします。コードをSQL ServerまたはOracleでシームレスに動作させることができます。1つはIDを使用し、もう1つはDALを介してシーケンスを使用します。私は同意します。複製を行ったり、後でデータを送信して後で処理したりする場合、GUIDが必要になることがあります。
私は常に代理キーを使用しました-「id」と呼ばれる自動インクリメント整数です。別のオプションが明らかな場合でも、これを行う理由はたくさんあります。
- 一貫性
- データに依存しない(一意、形式の変更によって破棄されない)
- 人間が読める
...そして合理的な理由なし:
- 結合の曖昧さ? -テーブルのエイリアスはより良い方法です、私見
- 最適なテーブル? -エントリごとに1バイトを削除すると、最適化が早すぎます、私見
- テーブルごとの決定? -一貫性がなくなりました
- スケーリングの問題? -え?なぜですか?
- 階層データ構造? -それは非正規化であり、まったく別の宗教の主題です。私は理論的にはいくつかの状況でファンだと言っても十分ですが、実際には決してありません:)
私がまだ考えもしなかった、またはまだ遭遇していないという理にかなった理由はいつでも歓迎します...
これは古典的な「依存する」ものです。すべてのプロジェクトに正しい答えはありません。さまざまな状況でさまざまなことが好きです。 ORMを使用しているかどうかと、ORMが何をサポートしているかによって異なります。全体的なアーキテクチャ(分散型または非分散型など)に依存します。動作すると思われるものを1つ選択し、タブとスペースについての議論に進みます。
サイズ、接続する人数、複数データベースサーバーの状況かどうかに応じて、オプション#1または#3を使用する傾向があります。
オプション2はあまり意味がありません。 3つのうちのいずれかが一意のレコードを識別するのに十分でない場合、2つのレコードが3つの列すべてに同じ値で表示される可能性があります。 3つの任意の組み合わせに一意性を適用する場合は、それらのインデックスを追加します。
自動インクリメントintまたはGUIDのみを使用しました。 99%の時間、自動インクリメントintを使用しました。データベースについて最初に学んだときに使用するように教えられたものであり、データベースを使用しない理由に遭遇したことはありません(GUIDが優れている理由はわかっていますが)。
読みやすくするため、自動インクリメントintが好きです。たとえば、「レコード129383を見てください」と言うことができます。そして誰かがそれを見つけて見つけるのはとても簡単です。ほぼ不可能なGUIDを使用します。
基本的な定義の答えを貼り付けてください。良い主キーを構成するものは、主に宗教と部屋の議論に委ねられています。個々の行に一意にマップされるもの、そして常にマップするものがある場合、それは主キーとして正常に機能します。その時点を過ぎて、他の考慮事項があります:
- 主キーの定義は複雑すぎませんか? 「ベストプラクティス」に従うために不必要な複雑さを導入することを避けますか?
- データベースが処理するオーバーヘッドが少ない(つまり、INTEGER対VARCHARなど)可能性のある主キーがありますか?
- 主キーの一意性と定義された不変性が変わらないことを絶対に確信していますか?
この最後の1つは、アドレス、電話番号、姓/名などに依存しているため、GUIDや自己増分整数列などを使用するようにほとんどの人を引き付ける可能性があります。私が考えることができる人々についての唯一の不変条件はSSNですが、それらが永遠にユニークであり続けることについては100%も確信していません。
うまくいけば、これはいくつかの明確さを追加するのに役立ちます...
主キーにアプローチする方法(そして最高だと思う)は、「デフォルト」を避けることです。アプローチ。これは、単に自動インクリメント整数をたたき、1日呼び出すだけでなく、問題を見て、「常に一意で変化しない列または列のグループがありますか?」答えが「はい」の場合、そのアプローチを取ります。
ほとんどの場合整数。
処理の小型化/高速化に加えて、他にも十分な理由があります。どちらを書き留めますか-「404040」または「3463b5a2-a02b-4fd4-aa0f-1d3c0450026c」?
わずかに関連性がありますが、小さな分類テーブル(本質的にコードでENUMを表すテーブル)があるときに最近始めた1つのことは、主キーをchar(3)またはchar(4 )。次に、それらの主キーをルックアップ値に代表させます。
たとえば、社内の販売代理店用の見積システムがあります。 「コストカテゴリ」があります。すべての見積品目には次のいずれかが割り当てられます。したがって、主キーが「MTL」、「SVC」、「TRV」、「TAX」、「ODC」である「tCostCategories」というタイプのルックアップテーブルがあります。ルックアップテーブルの他の列には、コードの通常の英語の意味、「Material」、「Service」、「Travel」、「Taxes」、「Other Direct Costs」などの詳細が格納されます。 。
intよりも多くのスペースを使用しないため、これは本当に素晴らしいことです。ソースデータを表示しているときは、値が何であるかを知るためにルックアップテーブルをリンクする必要はありません。たとえば、引用行は次のようになります。
1 PartNumber $ 40 MTL
2 OtherPartNumber $ 29.99 SVC
3 PartNumber2 $ 150 TRV
intを使用してカテゴリを表し、すべての行で1、2、3をリンクする方がはるかに簡単です-データは目の前にあり、パフォーマンスはまったく影響を受けないようです(本当にテストしたということです。)
実際の質問に関しては... RowGUID uniqueidentifiersが好きです。私はこれに100%ではありませんが、すべての行に内部RowGuidがありますか?その場合、RowGuidを使用すると、実際にはint(またはそれ以外の何か)よりもスペースが少なくなります。M$がGreatPlainsで使用するのに十分であれば、それで十分です。 (アヒルがいいですか?)
もう1つのGUIDを使用する理由-階層データ構造を使用しています。つまり、主キーが一致するテーブル「Company」とテーブル「Vendor」があります。しかし、会社からも「継承」する「製造元」という表もあります。ベンダーとメーカーに共通のフィールドは、これらのテーブルに表示されません-会社に表示されます。この設定では、intの使用はGuidsよりもはるかに苦痛です。少なくとも、ID主キーは使用できません。
私はそれらを信頼できるときはいつでも、自然キーが好きです。私は、主題の専門家にとって意味のあるキーを使用するために、小さなパフォーマンス価格を支払うつもりです。
エンティティを説明するテーブルには、主題と同じように個々のインスタンスを識別するシンプルで自然なキーが必要です。件名にエンティティの1つに対する信頼できる識別子がない場合、代理キーを使用します。
リレーションシップを記述するテーブルの場合、各コンポーネントがリレーションシップに参加するエンティティを参照する複合キーを使用します。したがって、エンティティテーブルの行を使用します。繰り返しますが、複合キーを使用した場合のパフォーマンスへの影響は通常最小限です。
他の人が指摘したように、「主キー」という用語は、少し誤解を招くです。リレーショナルデータモデルでは、使用される用語は「候補キー」です。 1つのテーブルに複数の候補キーが存在する場合があります。論理的には、それぞれが他と同じくらい優れています。それらのいずれかを「プライマリ」として選択します。そのキーを介してすべての参照を作成することは、設計者が選択できることです。
Guids.period。
スケールアウトする必要がある場合、または別の方法でプライマリキーを割り当てる必要がある場合は、それらが友人になります。他のすべてのインデックスを追加できます。
更新して、ステートメントを明確にします。
さまざまな種類のサイトに取り組んできました。小規模な単一サーバーの取引から、複数のDBサーバーとWebサーバーでバックアップされた大規模なサーバーまで。主キーとしてintを自動インクリメントすることで問題なく動作するアプリが確かにありました。しかし、それらは私が物事を行う方法のモデルに適合しません。
GUIDを使用すると、どこでもIDを生成できます。リモートサーバー、Webアプリ、データベース内、またはマルチマスター環境の複数のデータベース内でも生成できます。
一方、自動インクリメントされたINTは、プライマリデータベース内でのみ安全に生成できます。繰り返しになりますが、この1つのDBサーバーに密接に結び付けられるアプリケーションがあり、スケールアウトが心配することではない場合、これは問題ない可能性があります。
確かに、GUIDを使用すると、夜間にインデックスの再作成プロセスが必要になります。ただし、自動インクリメントINT以外を使用している場合は、とにかくそれを行う必要があります。ちなみに、プライマリとしてINTを使用している場合でも、断片化に対処するために再生成が必要な他のインデックスがある可能性があります。したがって、GUIDを使用しても、これらのタスクを実行する必要があるため、正確に別の問題が追加されるわけではありません。
大きなアプリを見ると、重要なことに気付くでしょう。それらはすべて、Base64エンコードのGUIDをキーとして使用しています。この理由は簡単です。GUIDを使用すると、 を簡単にスケールできますが、INTをスケールアウトしようとすると、多くのフープがジャンプする可能性があります。
最新のアプリでは、約1か月間、大量の挿入が行われます。その後、クエリの90 +%がすべてレポート用に選択されます。容量を増やすために、この大きな挿入期間中に追加のDBサーバーを起動できます。後で簡単にレポート用に単一のDBにマージします。 INTを使用してこれを実行しようとすると、絶対的な悪夢になります。
率直に言って、データベースをクラスター化またはレプリケーションをセットアップするときはいつでも、DBサーバーはテーブルにGUIDを要求します。そのため、システムを拡張する必要があると思われる場合は、適切なシステムを選択してください。
これは、気づいたかどうかにかかわらず、複雑なテーマです。このStackOverflow FAQのセクションに該当する場合があります。
ここではどのような質問をしてはいけませんか?
主観的、議論的、または詳細な議論が必要な質問は避けてください。これは答えられる質問のための場所です!
これは何年も議論されてきましたが、今後も議論され続けます。私が見たコンセンサスの唯一のヒントは、OOガイ(GUIDが唯一の方法です!)、データモデラー(ナチュラルキーが唯一の方法です!)、またはパフォーマンス指向のDBA(INTが唯一の方法です!)。