web-dev-qa-db-ja.com

主キーはどうですか?

私のチームでのかなり活発な議論の中で、私はほとんどの人が主キーとして何を好むかを考えさせられました。次のグループがありました。

  1. 自動インクリメントで十分な主キーであるInt/BigInt。
  2. 主キーを構成する少なくとも3つの列が必要です。
  3. Id、GUIDおよび人間が読み取れる行識別子はすべて、異なる方法で処理する必要があります。

PKの最善のアプローチは何ですか?あなたの意見を正当化できれば素晴らしいでしょう。上記より良いアプローチはありますか?

編集:誰でも簡単にサンプル/アルゴリズムを使用して、適切にスケーリングする行の人間が読み取れる識別子を生成できますか?

85
Perpetualcoder

時々接続するアプリを使用してデータベース間で同期を行う場合は、プライマリキーにGUIDを使用する必要があります。デバッグには苦痛が伴うので、その場合を除いて、自動インクリメントを行うintに固執する傾向があります。

自動インクリメントintがデフォルトであり、notを使用することは正当化されるべきです。

71
Bramha Ghosh

私は本当に基本的なポイントを指摘する答えを見ていません-つまり、主キーは同じ現実世界のエンティティのテーブルに2つのエントリを取得しないことを保証するものですデータベースでモデル化されています)。この観察結果は、主キーの良い選択と悪い選択の確立に役立ちます。

たとえば、(US)州の名前とコードの表では、名前またはコードのいずれかが主キーになる可能性があります-それらは2つの異なる候補キーを構成し、そのうちの1つ(通常は短い-コード)が選択されます主キー。機能的依存関係(および結合依存関係-1NFから5NF)の理論では、主キーではなく重要なのは候補キーです。

反例として、一般的に人間の名前は主キーとして不適切な選択をします。 「ジョン・スミス」という名前または他の似たような名前で行く人がたくさんいます。ミドルネームを考慮しても(覚えておいてください:誰もがミドルネームを持っているわけではありません-たとえば、私は持っていません)、複製の余地は十分にあります。したがって、人々は名前を主キーとして使用しません。社会保障番号(SSN)や従業員番号などの人工キーを発明し、それらを使用して個人を指定します。

理想的な主キーは、短く、ユニークで、記憶に残る、自然なものです。これらの特性のうち、一意性は必須です。残りは、実世界のデータの制約を考慮して柔軟にする必要があります。

したがって、特定のテーブルの主キーを決定することになると、そのテーブルが何を表しているのかを見なければなりません。テーブル内の列の値のセットは、テーブル内の各行を一意に識別しますか?これらが候補キーです。ここで、各候補キーが4列または5列で構成されている場合、それらの列があまりにも不格好であるため、適切な主キーを作成できないと判断する可能性があります。このような状況では、代理キー(人為的に生成された番号)を導入する場合があります。多くの場合(常にではありませんが)、代理キーには単純な32ビット整数で十分です。次に、この代理キーを主キーとして指定します。

ただし、mustは、他の候補キー(サロゲートキーも候補キーであり、選択した主キー)がすべて一意の識別子として維持されることを保証します-通常、一意の制約を配置することによりこれらの列のセット。

行を一意にするものを特定するのが難しい場合もありますが、情報を繰り返してもそれが真実ではないため、何かを行う必要があります。そして、注意を払わずに同じ情報を保存しようとする2つ(またはそれ以上)の行を取得し、その情報を更新する必要がある場合、1つの行のみを更新する危険性があります(特にカーソルを使用する場合)すべての行ではなく、行が同期していないため、どの行に正しい情報が含まれているかは誰にもわかりません。

いくつかの点で、これはかなり強硬な見方です。

GUID必要なときに使用しても特に問題はありませんが、big(非常に多くの場合、完全に適切な4バイトの値で十分です。GUIDを使用すると、4バイトの値で十分なディスクスペースが無駄になりますが、また、インデックスページあたりの値が少ないため、データへのインデックス付きアクセスも遅くなります。そのため、情報を取得するにはインデックスが深くなり、より多くのページを読み取る必要があります。

56

人々は普遍的な正しい答えを求めるため、これは宗教的な問題にすぎません。チームとこのSOスレッドの両方が非常に多くの意見の相違を示しているという事実は、さまざまな状況で説明するすべてのソリューションを使用する正当な理由があることを示す手掛かりになります。

  • 代理キーは、テーブル内の他の属性または属性セットが行を一意に識別するのに適していない場合に役立ちます。
  • 可能であれば、テーブルを人間が読みやすくするために、自然キーが優先されます。また、自然キーを使用すると、従属テーブルの外部キーに代理IDの代わりに実際の値を含めることができます。例えば。 state(CA、TX、NY)を保存する必要がある場合、intではなくchar(2)自然キーを使用することもできます。
  • 必要に応じて複合主キーを使用します。完全に適切な複合キーが存在する場合、「id」代理キーを不必要に追加しないでください(これは多対多のテーブルで特に当てはまります)。すべてのテーブルで3列のキーを使用することは、まったく無意味です。
  • GUIDは、複数のサイトで一意性を保持する必要がある場合のソリューションです。主キーの値が一意である必要があるが、順序付けられていないか連続している必要がない場合にも便利です。
  • INT vs. BIGINT:テーブル必須主キーの64ビット範囲は一般的ではありませんが、64ビットハードウェアの可用性が向上しているため、負担になることはなく、より確実になりますオーバーフローしないこと。 INTはもちろん小さいため、スペースが限られている場合は、わずかな利点があります。
24
Bill Karwin

The Database Programmer blog は、この種の情報のソースとして気に入っています。

主キーの3列ですか?ビジネスルールの要求に応じて、列には適切な一意の制約が必要ですが、別の代理キーが必要です。複合キーは、ビジネスロジックがキーに入ることを意味します。ロジックが変更されると、スキーマ全体がねじ込まれます。

20
duffymo

私は私のユニークが好きです。

15

常に代理キーを使用します。代理キー(通常はID列、自動インクリメント、またはGUID)は、キーがデータ自体に存在しないキーです。一方、自然キーは、それ自体で行を一意に識別するキーです。私が人生で言える限りでは、real自然キーはほとんどありません。米国のSSNのようなものでさえも自然な鍵ではありません。複合主キーは、発生を待つ災害です。そのデータを編集することはできません(これは、複合キーであろうとなかろうと、自然キーの主な欠点です)。なんて大きな無駄だ。

ここで、代理キーの選択のために、ID列を使用します(主にMS SQL Serverで作業します)。 GUIDは大きすぎるため、MicrosoftはagainstをPKとして使用することを推奨しています。複数のサーバーがある場合、必要なのは、10または20の増分、または同期/拡張する必要があると考えられるサーバーの最大数を増やし、後続の各サーバーの各テーブルのシードを含めることだけです。 、データの衝突は決してありません。

もちろん、インクリメントのために、ID列をBigInt(長い[64ビット]とも呼ばれます)にします。

少しの計算を行うと、インクリメントを100にしても、テーブルには92,233,720,368,547,758(> 92兆)行が残っています。

10
Robert C. Barth

「Primary」キーという語句での「Primary」という言葉の使用は、本当の意味で誤解を招く可能性があると思います。

まず、「キー」はテーブル内で一意でなければならない属性または属性セットであるという定義を使用します。

次に、キーを持つことは、いくつかの場合、相互に矛盾する目的に役立ちます。

  1. この親テーブルと関係がある子テーブル内の1つまたは複数のレコードへの結合条件として使用します。 (これらの子テーブルで外部キーを明示的または暗黙的に定義します)
  2. (関連)子レコードが親タブに親レコードを持たなければならないことを保証します; e(子テーブルFKは親テーブルにキーとして存在しなければなりません)
  3. テーブル内の特定のレコード/行を迅速に見つける必要があるクエリのパフォーマンスを向上させるため。

  4. 同じ論理エンティティを表す重複行がテーブルに挿入されないようにして、データの一貫性を確保します。 (これは「自然」キーと呼ばれることが多く、比較的不変のテーブル(エンティティ)属性で構成する必要があります。)

明らかに、意味のない完全な非自然キー(GUIDまたは自動生成された整数など)は、#4を満たすことはまったくできません。

しかし、多くの場合、多くの(ほとんどの)テーブルで、#4を提供できる完全に自然なキーは、多くの場合、複数の属性で構成され、幅が広すぎるか、または#1、#2、または#3の目的で使用すると受け入れられないことがありますパフォーマンスの結果。

答えは簡単です。両方を使う。他の子テーブルのすべての結合とFKに単純な自動生成整数キーを使用しますが、データの一貫性を必要とするすべてのテーブル(ごく少数のテーブルにはない)に、一貫性のないデータ行の挿入を防ぐ代替の一意のキーを確実に設定します。 ..さらに、両方を常に持っている場合、自然キーを使用することに反対するすべての異議(変更するとどうなりますか?FKとして参照されるすべての場所を変更する必要があります) ..矛盾する重複データを回避するために、PKである1つのテーブルでのみ使用しています...

GUIDについては、インデックスでGUIDを使用するとインデックスの断片化が発生する可能性があるため、GUIDの使用には十分注意してください。それらを作成するために使用される最も一般的なアルゴリズムは、GUIDの「ランダムな」部分を最上位ビット位置に配置します...これにより、新しい行が追加されるたびに通常のインデックスデフラグ/再インデックス付けの要件が増加します。

9
Charles Bretana

少し話題から外れていますが、私は...

主キーがGUIDの場合、do notクラスター化インデックスにします。 GUIDは非シーケンシャルであるため、ほとんどすべての挿入中にデータがディスク上に再配置されます。 (うん。)GUIDを主キーとして使用する場合は、非クラスター化インデックスにする必要があります。

9
Portman

絶対にすべきでないことの1つは、スマートキーを使用することです。それは、レコードに関する情報がキー自体にコード化されているキーであり、最終的にはあなたに噛み付くでしょう。

私は1つの場所で働きました。そこでは、主キーは文字と数字の組み合わせであるアカウントIDでした。具体的なことは覚えていませんが、たとえば、特定のタイプのアカウントは600の範囲にあり、別のタイプのアカウントは400から始まります。仕事の種類。または彼らがやった仕事の種類を変更しました。

別の場所。ツリーの場所をレコードの主キーとして使用しました。したがって、次のようなレコードがあります。

Cat1.subcatA.record1
Cat1.subcatA.record2
Cat1.subcatB.record1
Cat2.subcatA.record1

もちろん、顧客が最初に望んだのは、ツリー内のアイテムを移動する方法でした。ソフトウェアのセット全体は、それが起こる前に死にました。

私が今までに維持しなければならないコードを書いているなら、スマートキーを使わないでください。

8
thursdaysgeek

私は主キーとしての自動インクリメントのファンです。私はこれが警戒であることを心の底から知っていますが、データが追加されたとき(ORDER BY ID DESC、f'rインスタンス)にデータをソートすることをとても簡単にします。

3つの列は、人間が解析するのに非常に耳障りです。

そして、それはトレードオフです-リレーショナル機能をどれだけ必要とするか、この表の右をここで人間が質問することを理解できるようにすること(ストアドプロシージャまたはプログラムインターフェイスに対して)。

自動インクリメントは私たち人間のためのものです。 :-(

4

一般的に、それは依存します。

個人的には、自動インクリメントの整数が好きです。

しかし、私が言えることの1つは、他のソースからのデータをキーとして決して信用しないことです。私は誓った、それをするたびに、それは私に噛み付くように戻ってくる。まあ、二度と!

4
BoltBait

主キーを構成する少なくとも3つの列があるはずです。

これはわかりません。

「自然な鍵」について話しているのですか? 「名前と生年月日」?ナチュラルキーは存在する場合に理想的ですが、ナチュラルキーのほとんどの候補は一意ではない(同じ名前を持つ複数の人)か、一定ではありません(誰かが名前を変更できます)。

自動インクリメントで十分な主キーであるInt/BigInt。

Guidが好きです。自動インクリメントの潜在的な問題は、値(「order id」など)がデータベースインスタンス(「sales database」など)によって割り当てられることです...これは完全に機能しません(代わりに複合キーが必要になります)。複数のデータベースインスタンスによって作成されたデータをマージする必要があります(たとえば、それぞれが独自のデータベースを持つ複数の営業所から)。

3
ChrisW

RE GUIDの

これが本当に本当に本当になるかどうか気をつけてください[〜#〜] really [〜#〜]大きなデータベース、大量の負荷、高速アクセス。

私の最後の仕事では、1億から5億件のレコードのデータベースがありましたが、私たちのデータベース担当者はGUIDに強く反対し、適切なサイズの10進数を求めていました。彼らは、(Oracleの下では)文字列Guidの内部ストレージのサイズの違いと10進数の値がルックアップに非常に顕著な違いをもたらすと感じました。 (より大きなキー=トラバースするより深いツリー)

GUIDのランダムな性質により、インデックスページのフィルファクターも大幅に減少します。これにより、ティアリングとディスクI/Oが劇的に増加します。

3
John Chenault

自動インクリメント列。コードをSQL ServerまたはOracleとシームレスに動作させることができます。1つはIDを使用し、もう1つはDALを使用してシーケンスを使用します。私は同意します。複製を行ったり、後でデータを送信して処理したりする場合は、GUIDが必要になることがあります。

2
Otávio Décio

私は常に代理キーを使用しました-「id」と呼ばれる自動インクリメント整数です。別のオプションが明らかな場合でも、これを行う理由はたくさんあります。

  • 一貫性
  • データに依存しない(一意、フォーマットの変更によって破壊されない)
  • 人間が読める

...そして次のことをしない理にかなった理由はありません:

  • 結合のあいまいさ? -テーブルのエイリアスはより良い方法です、私見
  • 最適なテーブル? -エントリごとに1バイトを削除するのは時期尚早な最適化です、私見
  • テーブルごとの決定? -一貫性がなくなった
  • スケーリングの問題? -え?どうして?
  • 階層データ構造? -それは非正規化であり、他の宗教の主題です。私は理論的にはいくつかの状況でファンだと言っても十分ですが、実際には決してありません:)

私がまだ考えていない、またはまだ出会っていないという理にかなった理由はいつでも歓迎します...

2
jTresidder

私はそれらを信頼できるときはいつでも、自然なキーが好きです。私は、主題の専門家にとって意味のあるキーを使用するために、小さなパフォーマンス価格を喜んで支払います。

エンティティを説明するテーブルには、主題と同じ方法で個々のインスタンスを識別する単純な自然キーが必要です。主題にエンティティの1つに対する信頼できる識別子がない場合、代理キーに頼ります。

リレーションシップを説明するテーブルの場合、各コンポーネントがリレーションシップに参加するエンティティ、つまりエンティティテーブルの行を参照する複合キーを使用します。繰り返しますが、複合キーを使用した場合のパフォーマンスへの影響は通常最小限です。

他の人が指摘したように、「主キー」という用語は少し誤解を招くものです。リレーショナルデータモデルでは、使用される用語は「候補キー」です。 1つのテーブルに複数の候補キーが存在する場合があります。論理的には、それぞれが他と同じくらい優れています。それらのいずれかを「プライマリ」として選択し、そのキーを介してすべての参照を作成することは、設計者が選択できる選択です。

1
Walter Mitty

わずかに関連性がありますが、小さな分類テーブル(本質的にコードでENUMを表すテーブル)があるときに最近始めた1つのことは、主キーをchar(3)またはchar(4)にすることです。次に、これらの主キーをルックアップ値に代表させます。

たとえば、社内の販売代理店用の見積システムがあります。すべての見積品目に次のいずれかが割り当てられる「コストカテゴリ」があります。したがって、主キーが「MTL」、「SVC」、「TRV」、「TAX」、 「ODC」。ルックアップテーブルの他の列には、コードの通常の英語の意味、「材料」、「サービス」、「旅行」、「税金」、「その他の直接費用」などの詳細が格納されます。

Intよりも多くのスペースを使用しないため、これは本当に素晴らしいことです。ソースデータを表示しているときは、値が何であるかを知るためにルックアップテーブルをリンクする必要はありません。たとえば、引用行は次のようになります。

1部品番号$ 40 MTL
2 OtherPartNumber $ 29.99 SVC
3 PartNumber2 $ 150 TRV

Intを使用してカテゴリを表し、すべての行で1、2、3をリンクする方がはるかに簡単です-データは目の前にあり、パフォーマンスはまったく影響を受けません(私はそうではありません)本当にテストされました。)

本当の質問に関しては... RowGUID uniqueidentifiersが好きです。私はこれに100%ではありませんが、とにかくすべての行に内部RowGuidがありませんか??その場合、RowGuidを使用すると、実際にはint(またはそれ以外の何か)よりもスペースが少なくなります。M$がGreatPlainsで使用するのに十分であれば、それで十分です。 (アヒルがいいですか?)

1
Michael Bray

もう1つのGUIDを使用する理由-階層データ構造を使用しています。つまり、主キーが一致するテーブル「Company」とテーブル「Vendor」があります。しかし、私は会社からも「継承」する「メーカー」という表も持っています。ベンダーとメーカーに共通のフィールドは、これらのテーブルには表示されません-会社に表示されます。この設定では、intの使用はGuidsよりもはるかに苦痛です。少なくとも、ID主キーを使用することはできません。

1
Michael Bray

これは古典的な「依存」です。すべてのプロジェクトに正しい答えはありません。さまざまな状況でさまざまなことが好きです。 ORMを使用しているかどうかと、ORMが何をサポートしているかによって異なります。全体的なアーキテクチャ(分散型または非分散型など)に依存します。動作すると思われるものを1つだけ選択し、タブとスペースについての議論に進みます。

1
John Sheehan

私は、サイズ、接続する人数、および複数データベースサーバーの状況であるかどうかに応じて、オプション#1または#3を使用する傾向があります。

オプション#2はあまり意味がありません。 3つのうちのいずれかが一意のレコードを識別するのに十分でない場合は、2つのレコードが3つすべての列に同じ値で表示される可能性があります(余分なマシネーションを介さずに)。 3つの任意の組み合わせに一意性を適用する場合は、それらのインデックスを追加します。

1
BIBD

Guids.period。

スケールアウトする必要がある場合、または代替手段で主キーを割り当てる必要がある場合は、それらがあなたの友達になります。他のすべてのインデックスを追加できます。


私の声明を明確にするために更新します。

私はさまざまな種類のサイトに取り組んできました。小規模な単一サーバー取引から、複数のDBサーバーとWebサーバーでバックアップされた大規模サーバーまで。確かに、主キーとしてintを自動インクリメントすることで問題ないアプリがあったことは確かです。しかし、それらは私が物事を行う方法のモデルに適合しません。

GUIDを使用すると、どこでもIDを生成できます。リモートサーバー、Webアプリ、データベース内、またはマルチマスター環境の複数のデータベース内でも生成できます。

一方、自動インクリメントされたINTは、プライマリデータベース内でのみ安全に生成できます。繰り返しますが、このmightは、その1つのバッキングDBサーバーに密接に関連付けられているアプリケーションがあり、スケールアウトは関係ない場合は問題ありません。

確かに、GUIDを使用すると、毎晩インデックスの再作成プロセスが必要になります。ただし、自動インクリメントINT以外を使用している場合は、とにかくそれを行う必要があります。ちなみに、プライマリをINTとしても、断片化に対処するために再生成が必要な他のインデックスがある可能性があります。したがって、これらのタスクは関係なく実行する必要があるため、GUIDを使用しても別の問題が正確に追加されるわけではありません。

大きなアプリを見てみると、重要なことに気付くでしょう。それらはすべて、Base64エンコードのGUIDをキーとして使用しています。この理由は簡単です。GUIDを使用すると、outを簡単にスケーリングできますが、INTをスケールアウトしようとすると、多くのフープがジャンプする可能性があります。

私たちの最新のアプリは、約1か月続く大量の挿入期間を経ます。その後、クエリの90 +%がすべてレポート用に選択されます。容量を増やすために、この大規模な挿入期間中に追加のDBサーバーを起動できます。後でレポート用にそれらを単一のDBに簡単にマージします。 INTを使用してこれを実行しようとすると、絶対的な悪夢になります。

率直に言って、データベースをクラスター化するとき、またはレプリケーションをセットアップするときはいつでも、DBサーバーはテーブルにGUIDを要求します。したがって、システムを成長させる必要があると思われる場合は、適切なシステムを選択してください。

1
NotMe

自動インクリメントintまたはGUIDのみを使用しました。 99%の時間、自動インクリメントintを使用しました。これは、データベースについて最初に学んだときに使用するように教えられたものであり、データベースを使用しない理由に遭遇したことはありません(ただし、GUIDの方が良い理由はわかります)。

読みやすくするために、自動インクリメントintが好きです。たとえば、「レコード129383を見てください」と言えば、誰かがその中に入って見つけるのは非常に簡単です。 GUID=それはほとんど不可能です。

1
dtc

基本的な定義上の答えを超えて、good主キーを構成するものは、主に宗教と部屋の議論に委ねられています。個々の行に一意にマップされるものがあり、常にマップされる場合、それは主キーとして正常に機能します。その時点を過ぎて、他の考慮事項があります:

  • 主キーの定義は複雑すぎませんか? 「ベストプラクティス」に従うために、不要な複雑さを導入することを避けますか?
  • データベースが処理するために必要なオーバーヘッドが少ない(つまり、INTEGER対VARCHARなど)可能性のある主キーがありますか?
  • 主キーの一意性と定義された不変性が変わらないことを絶対に確信していますか?

住所、電話番号、姓/名などに依存しているため、GUIDや自己増分整数列などを使用するように、ほとんどの人はこの最後のものを使用する可能性があります。私が考えることができる人々についての唯一の不変条件はSSNですが、それから私はそれらが永久にユニークであり続けることについて100%さえ確実ではありません。

うまくいけば、これはいくつかの明快さを追加するのに役立ちます...

1
Ed Carrel

私が主キーにアプローチする方法(そして、私が最高だと思う)は、「デフォルト」アプローチを避けることです。これは、単に自動インクリメントする整数をたたき、それを1日呼び出すのではなく、問題を見て、「常に一意で変更されない列または列のグループがあるか」と言うことを意味します。答えが「はい」の場合、そのアプローチを取ります。

1

ほとんど常に整数。

処理がより小さく/速くなることに加えて、他の正当な理由があります。 「404040」と「3463b5a2-a02b-4fd4-aa0f-1d3c0450026c」のどちらを書き留めますか?

1
user42092

これは、気づいているかどうかにかかわらず、複雑なテーマです。このStackOverflow FAQのセクションに該当する場合があります。

ここではどのような質問をしてはいけませんか?

主観的、議論的、または詳細な議論が必要な質問は避けてください。これは答えられる質問のための場所です!

これは何年も議論されており、何年も議論され続けます。私が見たコンセンサスの唯一のヒントは、OO guy(GUIDが唯一の方法です!)、データモデラー(Naturalキー唯一の方法です!)、またはパフォーマンス指向のDBA(INTが唯一の方法です!)。

0
Shane Delmore