常に自動インクリメント整数のプライマリキーを持つことは良い習慣ですか?


191

私のデータベースでは、id特定の行を一意に検索できるように、作成するすべてのテーブルの名前に整数の主キーを自動的にインクリメントするという習慣になりがちです。

これは悪い考えと考えられますか?この方法で行うことには欠点がありますか?場合によっては、一意の識別子id, profile_id, subscriptionsがどこにあるか、テーブルの外部へのリンクなどの複数のインデックスがあります。idprofile_ididProfile

または、そのようなフィールドを追加したくないシナリオはありますか?


61
単純な自動インクリメント識別子が問題である例については、ドイツの戦車の問題をご覧ください。もちろん、これはパブリックでIDを使用している場合にのみ重要です。
ベルギ

24
@ArukaJ重要なのは、システムに関するいくつかの情報が漏洩することです。たとえば、データベースにユーザー作成の投稿が含まれており、各投稿に連続IDが割り当てられているとします。4つの投稿を作成し、それぞれが午前4時(20)、午前5時(25)、午後8時(100)、午後9時(200)のIDを取得するとします。IDを見ると、午前4時から午前5時までに5つの投稿のみが追加され、午後8時から午後9時までに100の投稿が追加されたことがわかります。サービス拒否攻撃の時間を選択しようとした場合、貴重な情報になる可能性があります。
ジョシュアテイラー

29
「ドイツ戦車の問題」について不満を言う人すべてに....誰かがデータにアクセスすることを禁止する唯一のことはあなたのURLのキーです...あなたはGUIDとAuto INTより大きな問題を抱えています。
マシューホワイト化

11
@MatthewWhitedこれは、URLのパラメーターを交換するだけではありません。あなたがサイトを使用している時に資産100を作成しt、資産120 を作成したとしますt + 60。これらのID(100および120)の両方を難読化されていない形式で表示できる場合、存在するアセットの総数と、おおよそ作成されるレートがわかります。これは情報漏洩です。これは単なる仮説ではありません。
クリスヘイズ

15
常に練習するのは良い習慣ですか?」いいえ
-brian_o

回答:


137

一意の行識別子を保証することは決して悪い考えではありません。決して言ってはいけないことだと思いますが、圧倒的多数の時間を使ってみましょう。

理論的な潜在的な欠点には、維持するための追加のインデックスと使用される追加のストレージスペースが含まれます。それを使用しない理由はこれだけでは十分ではありません。


11
それが私がすることです。ほとんどの人は「id」または「tablename_id」(user_idなど)を使用します。引数は通常、列が必要な場合ではなく、どのように名前を付けるかです。
GrandmasterB

103
個人的には、テーブル名は残りを意味するべきだと思います。TableName.idとは対照的にTableName.TableName_id、それは他に何idを指すのですか?テーブルに別のidフィールドがある場合、他のテーブルを参照している場合はテーブル名をプレフィックスとして付けます
-AJJ

10
@ArukaJは、SQLiteを使用していると述べました。これは実際には少し特別なケースです。常にそのような列が「内部」にあるためです。余分なスペースを使用することはありません。必要な場合でも必要なスペースを確保できるからです。また、SQLiteのROWIDは常に64ビット整数です。私の理解が正しい場合、自動インクリメント行を定義すると、それは内部ROWIDのエイリアスになります。だから、あなたはいつもそれをやっていたかもしれません!参照してくださいsqlite.org/autoinc.html
GrandmasterB

9
私が考えることができる1つの例外は、他の方法で生成された一意の識別子がある場合です。その場合、それは主キーである必要があり、自動インクリメントIDは冗長です。
HamHamJ

4
@GrandmasterB:SQLiteの現在のバージョンでは、最適化としてWITHOUT ROWID(明示的にPRIMARY KEY)テーブルを作成できます。ただし、それ以外の場合、INTEGER PRIMARY KEY列はROWIDのエイリアスです。
dan04

92

以前のすべての答えに同意しません。すべてのテーブルに自動インクリメントフィールドを追加することはお勧めできませんが、多くの理由があります。

明らかなキーがないテーブルがある場合、自動インクリメントフィールドは良い考えのようです。結局のところ、あなたはしたくないselect * from blog where body = '[10000 character string]'。むしろあなたがしたいselect * from blog where id = 42。これらの場合のほとんどで、本当に必要なのは一意の識別子であると主張します。連続した一意の識別子ではありません。代わりに、普遍的に一意の識別子を使用する必要があります。

ほとんどのデータベースには、ランダムな一意の識別子を生成する関数があります(uuidmysql、postgres newid、mssql)。これらを使用すると、異なるマシン上の複数のデータベースにデータをいつでも生成でき、それらの間のネットワーク接続はなく、競合なしでデータをマージできます。これにより、たとえばマイクロサービスなどを使用して、複数のサーバーやデータセンターをより簡単にセットアップできます。

また、これにより、攻撃者がアクセスすべきでないページのURLを推測することを防ぎます。がある場合は、https://example.com/user/1263おそらく https://example.com/user/1262もあります。これにより、ユーザープロファイルページでセキュリティの悪用を自動化できます。

uuidカラムが役に立たなかったり、有害でさえある場合も多くあります。ソーシャルネットワークがあるとします。usersテーブルとテーブルがありfriendsます。friendsテーブルには、2つのユーザーID列と1つの自動インクリメントフィールドが含まれます。の3友達になりたい5ので3,5、データベースに挿入します。データベースは、自動インクリメントIDとstoresを追加します1,3,5。どういうわけか、ユーザー3は「友達を追加」ボタンをもう一度クリックします。3,5データベースに再度挿入すると、データベースは自動インクリメントIDを追加してinsertを挿入します2,3,5。しかし、今35お互いに二度友達です!これはスペースの無駄です。考えてみると、自動インクリメント列も無駄です。あなたがどうかを確認する必要があるすべてabこれらの2つの値を持つ行を選択することです。これらは合わせて、一意の行識別子です。(おそらく、重複排除のためにいくつかのロジックを作成する必要が3,5あり5,3ます。)

url-shortenerを作成するときのように、シーケンシャルIDが有用な場合もありますが、ほとんどの場合(URL短縮機能を使用する場合でも)ランダムに生成された一意のIDを代わりに使用したいです。

TL; DR:各行を一意に識別する方法がまだない場合は、自動インクリメントの代わりにUUIDを使用します。


26
UUIDの問題は、UUIDが大部分のテーブルに対してスペースを取りすぎることです。各テーブルに正しい一意の識別子を使用します。
スティーブン

49
一意性に関する段落全体は議論の余地がありません-主キーの有無にかかわらず、一意性を強制できます。その上、理論的にはUUIDの方が優れていますが、DBAタスクをデバッグ/実行するとき、または「攻撃に抵抗しない」ことを行うときに使用するにはひどいです。

11
UUIDが優れている別のシナリオ:i等のPUT操作を実装し、重複行を導入することなく安全に要求を再試行できるようにします。
yurez

21
「URL推測」の点で、一意のID(シーケンシャルまたはその他)を持つことは、そのIDをアプリケーションのユーザーに公開することを意味しません。
デイブ・シェーロマン

7
純粋にデータベースの観点からすると、この答えは完全に間違っています。自動インクリメント整数の代わりにUUIDを使用すると、インデックスが非常に速く成長し、パフォーマンスとメモリ消費に悪影響を及ぼします。WebサービスまたはWebアプリの観点から話している場合、とにかくデータベースとフロントエンドの間にレイヤーが必要です。それ以外は悪いデザインです。データを主キーとして使用するのはさらに悪いことです。主キーはデータ層でのみ使用し、他の場所では使用しないでください。
酒に酔ったコードモンキー

60

Autoincementalキーには主に利点があります。

ただし、考えられる欠点は次のとおりです。

  • ビジネスキーがある場合は、ビジネスルールを適用するために、その列にも一意のインデックスを追加する必要があります。
  • 2つのデータベース間でデータを転送する場合、特にデータが複数のテーブル(マスター/詳細)にある場合、データベース間でシーケンスが同期されないため、簡単ではありません。最初に、を使用して等価テーブルを作成する必要があります起点データベースのどのIDがターゲットデータベースのどのIDに対応するかを知るための一致としてのビジネスキー。ただし、分離されたテーブルとの間でデータを転送する場合、これは問題になりません。
  • 多くの企業には、アドホック、グラフィカル、ポイントアンドクリック、ドラッグアンドドロップのレポートツールがあります。自動増分IDは無意味であるため、このタイプのユーザーは、「アプリ」以外のデータを理解するのが難しいと感じるでしょう。
  • 誤ってビジネスキーを変更した場合、その行を回復することはできなくなる可能性があります。これは、人間がそれを識別するための何かをもう持っていないためです。これはBitCoinプラットフォームで一度障害を引き起こしました
  • 一部の設計者は、PKを2つの外部IDで単純に構成する必要がある場合に、2つのテーブル間の結合テーブルにIDを追加します。明らかに、結合テーブルが3つ以上のテーブルの間にある場合、自動増分IDは意味をなしますが、FKの組み合わせに適用してビジネスルールを適用する場合は、一意のキーを追加する必要があります。

ここに、代理キーの欠点に関するウィキペディアの記事セクションがあります。


13
代理キーのmt.goxの欠陥を非難するのは、かなり疑わしいようです。問題は、複合キーにすべてのフィールドが含まれていて、可変/不正フィールドも含まれいたことです。
CodesInChaos

6
自動インクリメントキーを使用することの「社会的」な欠点は、「ビジネス」が時々ギャップがないことを前提とし、挿入の失敗(トランザクションロールバック)が発生したときに欠落行がどうなったかを知ることを要求することです。
リックライカー

4
もう1つの欠点は、システムが大きくなりすぎてデータベースを分割する必要がある場合、自動インクリメントを使用してグローバルに一意のキーを生成できなくなることです。そのポイントに到達すると、その仮定に依存する多くのコードを持っているかもしれません。データベースが分割されている場合に機能し続ける一意の識別子を生成する他の方法があります。
カスペルド

1
@Voo選択したデータベースがそれをサポートすることは保証されません。また、データベース自体よりも上位層に実装しようとすると、SQLが提供する保証の一部が失われます。最後に、分散システムを使用している場合、IDの集中化された割り当てにより遅延が増加します。
カスペルド

1
@Vooもちろん、システムの規模に関係なく、自動インクリメントされたIDの性質についてあまり多くの仮定を立てるべきではありません。データベースが1つしかない場合、それらは順番に割り当てられますが、順番にコミットされる保証はありません。また、すべてのトランザクションがコミットされるわけではないため、シーケンスにギャップが生じる可能性があります。
カスペルド

20

反対に、いいえ、常に数値のAutoInc PKを持っている必要はありません。

データを慎重に分析すると、多くの場合、データ内の自然キーを識別します。これは、データがビジネスにとって本質的な意味を持つ場合によくあります。PKは、システムの属性を記述するためにビジネスユーザーが第二言語として利用する古代のシステムの成果物である場合があります。たとえば、車両管理システムの「車両」テーブルの主キーとして使用される車両VIN番号を見てきました。

独自の識別子を既に持っている場合は、それが使用されている場合、それが発生しました。2番目の無意味な主キーを作成しないでください。無駄であり、エラーを引き起こす可能性があります。

AutoInc PKを使用して、ポリシー番号などの顧客にとって意味のある値を生成できる場合があります。開始値を適切な値に設定し、先行ゼロなどに関するビジネスルールを適用します。これはおそらく「両方の世界のベスト」アプローチです。

比較的静的な値が少数ある場合は、システムユーザーにとって意味のある値を使用します。保険の「保険種別」コンテキストでL、H、CがLife、Car、Homeを表すL、C、Hを使用できる場合、またはVINの例に戻って「TO 「トヨタの?Toyataのすべての車には、「TO」で始まるVINがあります。ユーザーが覚えておくべきことが1つ減り、プログラミングやユーザーエラーが発生する可能性が低くなります。書くこと、そしておそらくより速く生成すること。

これのさらなる開発はおそらく「あまりにも大きな橋」であり、一般的にはお勧めしませんが、完全を期すためにこれを含めています。つまり、説明を主キーとして使用します。急速に変化するデータの場合、これは憎悪です。以下のための非常に報告された静的データのすべての時間、そうでないかもしれません。言及するだけで、可能性としてそこに座っています。

私はAutoInc PKを使用しています。頭をかき回し、より良い選択肢を最初に探します。データベース設計の技術は、意味のあるものを作成しており、すぐに問い合わせることができます。結合が多すぎると、これが妨げられます。

編集自動生成PKを必要としないもう1つの重要なケースは、他の2つのテーブルの共通部分を表すテーブルのケースです。車の類推に固執するために、A Carには0..nのアクセサリーがあり、各アクセサリーは多くの車にあります。そのため、これを表すには、CarおよびAccessoryからのPKおよびリンクの日付などに関するその他の関連情報を含むCar_Accessoryテーブルを作成します。

(通常)必要ないのは、このテーブルのAutoInc PKです。「この車にはどのアクセサリーが入っているか教えてください」、または「この車にはどのアクセサリーがあるのか​​教えてください」というアクセサリーからのみアクセスできます。


4
>すべてのToyata車には、「TO」で始まるVINがありますが、これは事実ではありません。日本製の場合、「JT」で始まります。アメリカの内蔵トヨタは完全に異なるヴァン持っen.wikibooks.org/wiki/...
モンティ・ハーダー

17
Don't create a second, meaningless primary key; it's wasteful and may cause errors.ただし、レコードの一意性を確立する方法が6列の組み合わせである場合、6列すべてを常に結合すると、エラーが発生しやすくなります。データには当然PKがありますが、id列とそれらの6列に一意の制約を使用する方が適切です。
ブラッド

14
これらの提案のいくつかは、私にとっては少し遠いものだと認めています。はい、実用的であることは問題ありませんが、ドメインの一部の属性が残りの日の間ユニークであり続けるだろうと誰かが長子の人生を誓った頻度を数えることはできません。まあ、通常、それはライブになってから2週間目まで、最初の複製が見つかったときにうまくいきました。;)PKとして「説明」を使用することは、かなり遠いです。
AnoE

2
@Monty、私の悪い、あなたは正しい。間違いのないメモリ。フリート管理システムを設計してから20年です。いいえ、VINは主キーではありませんでした:) AutoInc Asset_ID IIRCを使用しました。たとえば、自動車とアクセサリー(サンルーフなど)をリンクする多対多の関係のリンカーであるテーブルAutoInc PK。
mcottle

7
本当に不変の「自然キー」がほとんどないことは本当に驚くべきことです。SSN's?いいえ、変更できます。それはまれですが、起こる可能性があります。ユーザー名?いや。最終的には、誰かが変更する正当なビジネス上の理由を持つことになります。VINは多くの場合教科書の例ですが、他の多くはありません。通りの名前が変更されると、自宅の住所も変更される場合があります。
エリックFunkenbusch

12

多くのテーブルにはすでに自然な一意のIDがあります。これらのテーブルに別の一意のID列(自動インクリメントまたはその他)を追加しないでください。代わりに自然な一意のIDを使用してください。別の一意のIDを追加する場合、基本的にデータに冗長性(重複または依存関係)があります。これは、正規化の原則に反します。1つの一意のIDは、正確性のために他のIDに依存しています。これは、彼らがで同期して完全に保たれなければならないことを意味し、すべての回すべてのシステムこれらの行を管理します。データの整合性におけるもう1つの脆弱性は、長期にわたって管理および検証する必要がないことです。

最近のほとんどのテーブルでは、一意のid列を追加することでパフォーマンスが大幅に向上する必要はありません(パフォーマンスが低下することもあります)。ITの一般的なルールとして、ペストのような冗長性は避けてくださいそれがあなたに提案されるどこでもそれに抵抗しなさい。嫌悪感です。そして、引用に注意してください。すべてをできるだけシンプルにする必要がありますが、シンプルではありません。自然なIDが整頓されていないように見えても、1つで十分な2つの一意のIDを持たないでください。


3
決して変更されないことが絶対に保証されている場合にのみ、「自然な」IDを主キーとして使用するべきではありませんか?たとえば、運転免許証番号を主キーとして使用すべきではありません。なぜなら、人が新しい運転免許証を取得した場合、そのテーブルだけでなく、それを参照する外部キーを持つテーブルを更新する必要があるからです!
エコリス

1
運転免許証番号が自然な一意のIDとして認められない理由はいくつかあります。まず、それらの一部は、誕生日や名前など、他のデータから派生しています。それらは州全体で一意であるとは保証されません。また、例を挙げると、同じ番号のライセンスが再発行された場合、おそらく有効期限が延長された場合、どうなりますか?同じ番号の異なるライセンスがあります。自然IDは、主キーの基本的なプロパティを満たす必要があります。(少なくとも米国では)運転免許証番号には、この点でいくつかの欠点があります。
ブラッドトーマス

1
OK、私は自然なIDの定義を誤解したと思います。実際に不変であることが保証されているかどうかに関係なく、ビジネスルールで定義された単なるIDであると考えました。
エコリス

10

大規模なシステムでは、IDは一貫性を高めるため、ほとんどどこでも使用できます。このコンテキストでは、個々の主キーは推奨されません。最終的には高価です(理由をお読みください)。

すべてのルールには例外があるため、エクスポート/インポートに使用されるステージングテーブル、および同様の一方向テーブルまたは一時テーブルで整数の自動インクリメントIDを必要としない場合があります。また、分散システムではIDではなくGUIDを好むでしょう。

ここでの多くの回答は、既存の一意のキーを使用する必要があることを示唆しています。150文字の場合でも?そうは思いません。

今私の主なポイント:

自動インクリメント整数IDの反対者は、最大20個のテーブルを持つ小さなデータベースについて話しているようです。そこで、各テーブルに対して個別のアプローチを行う余裕があります。

しかし、400以上のテーブルを持つERPがあれば、整数の自動インクリメントIDをどこにでも持つことができます(上記の場合を除く)他の一意のフィールドが存在し、一意性が確保されている場合でも、それらに依存することはありません。

  • 普遍的な時間節約、労力節約、覚えやすいコンベンションの恩恵を受けます。
  • ほとんどの場合JOIN、キーが何であるかをチェックする必要なしにテーブルを作成します。
  • 整数の自動インクリメント列で動作するユニバーサルコードルーチンを使用できます。
  • 既存のテーブルのIDを参照するだけで、以前は予見されなかった新しいテーブルまたはユーザープラグインでシステムを拡張できます。それらは最初からすでに存在し、追加する費用はかかりません。

大規模なシステムでは、これらの個々の主キーの小さな利点を無視し、ほとんどの場合、整数の自動インクリメントIDを一貫して使用する価値があります。既存の一意のフィールドを主キーとして使用すると、レコードごとに数バイト節約できますが、追加のストレージまたはインデックス作成時間は、今日のデータベースエンジンでは問題になりません実際、開発者/保守者の無駄な時間に多くのお金とリソースを失っています。今日のソフトウェアは、プログラマーの時間と労力に合わせて最適化する必要があります。一貫性のあるIDを使用するアプローチは、はるかに優れています。


個人的な経験から、私はあなたの答えの後半に心から同意します。グローバルで一意のキーが必要になるのは、高速でコンパクトなインデックスが必要になる場合よりもはるかに少ないでしょう。必要な場合は、自動生成されたIDとUUID列を持つGlobalEntitiesテーブルを作成します。次に、たとえば、ExGlobalEntityId外部キーをCustomersテーブルに追加します。または、いくつかの値のハッシュを使用します。
酒に酔ったコードモンキー

8

余計な設計はお勧めできません。つまり、不要な場合は常に主キーを自動インクリメントすることはお勧めできません。

不要な例を見てみましょう。

記事用のテーブルがあります。これには、int主キーid、およびという名前のvarchar列がありtitleます。

また、記事のカテゴリでいっぱいのテーブルがありますid。int主キーvarchar nameです。

Articlesテーブルの1行にはid、5とtitle 「ガチョウをバターで調理する方法」があります。その記事をCategoriesテーブルの次の行にリンクします: "Fowl"(id:20)、 "Goose"(id:12)、 "Cooking"(id:2)、 "Butter"(id:9) 。

これで、記事とカテゴリの2つのテーブルができました。2つの間の関係はどのように作成しますか?

id(主キー)、article_id(外部キー)、category_id(外部キー)の3つの列を持つテーブルを作成できます。しかし、今は次のようなものがあります:

| id | a_id | c_id |
| 1 | 5 | 20 |
| 2 | 5 | 12 |
| 3 | 5 | 2 |

より良い解決策は、2つの列で構成される主キーを持つことです。

| a_id | c_id |
| 5 | 20 |
| 5 | 12 |
| 5 | 2 |

これは以下を行うことで実現できます。

create table articles_categories (
  article_id bigint,
  category_id bigint,
  primary key (article_id, category_id)
) engine=InnoDB;

自動インクリメント整数を使用しないもう1つの理由は、主キーにUUIDを使用している場合です。

UUIDは定義上一意であり、一意の整数を使用した場合と同じことを実現します。また、整数に対して独自の追加の利点(および短所)もあります。たとえば、UUIDを使用すると、参照している一意の文字列が特定のデータレコードを指していることがわかります。これは、1つの中央データベースがない場合、またはアプリケーションがデータレコードをオフラインで作成する機能を備えている場合(後でデータベースにアップロードする場合)に便利です。

最終的に、主キーを物事として考える必要はありません。それらを実行する機能と考える必要があります。なぜ主キーが必要なのですか?将来変更されないフィールドを使用して、テーブルから特定のデータセットを一意に識別できるようにするため。idこれを行うために呼び出される特定の列が必要ですか、それとも他の(不変の)データに基づいてこの一意の識別を行うことができますか?


7

または、そのようなフィールドを追加したくないシナリオはありますか?

承知しました。

まず第一に、自動インクリメントを持たないデータベースがあります(たとえば、Oracleは確かに周りの最小の競合の1つではありません)。これは、誰もがそれらを好むまたは必要とするわけではないことを示す最初の兆候です。

さらに重要なのは、IDが実際に何を考えている -それはあなたのデータの主キーです。別の主キーを持つテーブルがある場合、IDは必要ありません。たとえば、テーブル(EMPLOYEE_ID, TEAM_ID)(各従業員が複数のチームに同時に参加できる場合)には、これら2つのIDで構成される明確に定義された主キーがあります。IDこのテーブルの主キーでもある自動インクリメント列を追加しても、まったく意味がありません。これで、2つの主キーを持ち歩くことができます。「主キー」の最初の単語から、本当に1つだけが必要であるというヒントが得られます。


9
(Oracleユーザーではないので質問は許しませんが)Oracleは他の人がAutoincrement / Identityを使用するのと同じ方法でSequenceを使用しませんか?OracleにはAutoincrementデータ型が実際には単なるセマティックな引数がないと言っていますか?
ブラッド

まあ、それはほんの小さな点でした。主な部分は、実行IDがすべてのテーブルに適しているわけではないことです。したがって、すべてのテーブルで自動IDをスラップすることに慣れるのは賢明ではないかもしれません。
AnoE

どの2つの主キーは、彼らがあまりにとして主キーを果たすことができれば候補キーと呼ばれている1つのプライマリキーとすべての残りの部分があり、存在しない..
ラーフルのティアギ(Tyagi)

7

「長命」データ(ビットフィールドを設定して「論理的に削除」されたとしても、一度挿入して無期限に保持する予定のレコード)の新しいテーブルを定義するときは、通常「ID」列(自動更新整数)を使用します)。

使用したくないときに考えられる状況がいくつかあります。そのほとんどは、DBの1つのインスタンス上の1つのテーブルが新しいID値の信頼できるソースにならないシナリオになります。

  • 潜在的な攻撃者にとってインクリメンタルIDが多すぎる情報になる場合。「公共向け」データサービスにID列を使用すると、「ドイツ戦車問題」に対して脆弱になります。レコードID 10234が存在する場合、レコード10233、10232などが存在し、少なくともレコード10001に戻っていることがわかります。その後、レコード1001、101、1を確認して、ID列がどこから始まったのかを簡単に確認できます。主にランダムなデータで構成されるV4 GUIDは、このインクリメンタル動作を設計上破壊します。そのため、1つのGUIDが存在するからといって、GUIDのバイトをインクリメントまたはデクリメントすることで作成されたGUIDは必ずしも存在しないため、攻撃者が意図したサービスを使用するのが難しくなりますダンプツールとしての単一レコードの取得用。アクセスをより制限できるセキュリティ対策は他にもありますが、これは役立ちます。
  • M:M相互参照テーブル。これは一種のギミーですが、私はそれを以前に見ました。データベース内の2つのテーブル間に多対多の関係がある場合、適切なソリューションは、各テーブルのPKを参照する外部キー列を含む相互参照テーブルです。このテーブルのPKは、組み込みインデックスの動作を取得し、参照の一意性を確保するために、実質的に常に2つの外部キーの複合キーである必要があります。
  • このテーブルで大量の挿入と削除を大量に計画する場合。おそらく、ID列の最大の欠点は、元のテーブルのキー値を維持したい別のテーブルまたはクエリから行を挿入するときに経験しなければならない余分な問題です。「ID挿入」をオンにする必要があります(DBMSで行われます)。次に、挿入するキーが一意であることを手動で確認し、インポートが完了したら、IDカウンターをテーブルのメタデータを存在する最大値まで。このテーブルでこの操作が頻繁に発生する場合は、別のPKスキームを検討してください。
  • 分散テーブル用。ID列は、単一インスタンスデータベース、フェールオーバーペア、および特定の時点で1つのデータベースインスタンスがデータスキーマ全体に対する唯一の権限であるその他のシナリオに最適です。ただし、1台のコンピューターで十分な速度を維持しながら移動できるのは非常に大きいだけです。レプリケーションまたはトランザクションログ配布により、追加の読み取り専用コピーを取得できますが、そのソリューションの規模にも制限があります。遅かれ早かれ、データの挿入を処理し、相互に同期する2つ以上のサーバーインスタンスが必要になります。ほとんどのDBMSは、インスタンス固有の識別子として生成するGUIDの一部を使用するように事前に構成されているため、増分ではなくGUIDフィールドが必要になります。その後、残りの識別子をランダムに生成します。または増分。どちらの場合にも、
  • DBの複数のテーブルに一意性を適用する必要がある場合。会計システムでは、たとえば、総勘定元帳(これまでに発生したすべてのアカウントのクレジットまたはデビットごとに行があるため、非常にすばやく大きくなります)をそれぞれ1暦月/月を表すテーブルのシーケンスとして管理することが一般的です年。その後、ビューを作成して、レポート用にそれらを一緒にフックできます。論理的には、これはすべて1つの非常に大きなテーブルですが、それを切り詰めることで、DBのメンテナンスジョブが簡単になります。ただし、重複キーで終わることなく、複数のテーブルへの挿入を管理する方法の問題を提示します(最後のトランザクションを閉じながら、翌月にトランザクションのロギングを開始できるようにします)。繰り返しになりますが、DBMSはこれらを真にユニークな方法で生成するように設計されているため、ID整数列ではなくGUIDが最適なソリューションです。

願わくば述べたように、これらの状況でID列を使用できる回避策がありますが、ほとんどの場合、ID整数列からGUIDへのアップグレードはより簡単で、問題をより完全に解決します。


1
M:NリレーションのID, ID_M, ID_Nインスタンスにプロパティをアタッチするため、M:Nテーブル(列を使用)でIDが必要な場合があります。
ミロクスラフ

V4 GUIDSは暗号的に強力なPNRGを使用することを保証されていないため、最初の例のimoに実際に依存するべきではありません(ただし、dbエンジンがより強力な約束をする場合は大丈夫かもしれませんが、それはむしろ移植性がありません)。それ以外の場合は合理的な投稿。
Voo

1
@miroxlav-2つのFKの外側にある別のPKが良いアイデアであるという関係に関して、テーブルに十分な追加メタデータがある場合、それはもはや相互参照テーブルではないと断言します。他の2つを参照するのは、それ自身のエンティティです。
キース

@Voo-そうです、V4 GUIDは暗号的にランダムであることが保証されておらず、一意であるだけです(すべてのGUIDがそうであるように)。ただし、米国のジェット戦闘機のテール番号は、暗号的にランダムなシードデータ/アルゴリズムからも生成されません。本当に探しているのは、まばらな人口のドメインです。V4 GUIDには112バイトのランダムデータがあり、5e33レコードを一意に識別できます。
キース

その数を見ると、地球上のすべての男性、女性、子供(すべて70億人)が、DBで741 個の個別にカタログおよびID化されたデータポイントを持つことができ、使用可能な10億あたり1つのGUID値しか使用していません。グローバル産業としてのビッグデータは、この規模の知識にさえ近いものではありません。GUID生成のパターンを指定しても、データがシステムに入りGUIDが割り当てられる順序など、他のエントロピーのソースが関係します。
キース

7

自動インクリメント(ID)主キーは、データベースおよびそのデータベースの直接のクライアントのコンテキストの外では意味がないことに注意することを除いて、良いアイデアです。たとえば、データの一部を別のデータベースに転送して保存した後、両方のデータベーステーブルに異なるデータを書き込むと、IDが分岐します。つまり、1つのデータベースのIDが42のデータは必ずしもデータと一致しませんもう一方のIDは42です。

これを考えると、データベースの外部で行を一意に識別できる必要がある場合(および頻繁に識別される場合)、この目的のために別のキーが必要です。慎重に選択されたビジネスキーでもかまいませんが、多くの場合、一意性を保証するために必要な多数の列の位置になります。別の手法は、ID列を自動インクリメントクラスター化プライマリキーとして使用し、別のuniqueidentifier(guid)列を非クラスター化ユニークキーとして使用して、行が世界中のどこに存在するかを一意に識別することです。この場合に自動インクリメントキーがまだある理由は、GUIDに同じ操作を行うよりも、自動インクリメントキーをクラスター化してインデックス化する方が効率的だからです。

自動インクリメントキーが必要ない場合の1つのケースは、主キーが他の2つのテーブルのId列の複合である多対多テーブルです(ここでも自動インクリメントキーを使用できますが、その要点がわからない)。

もう1つの質問は、自動増分キーのデータ型です。Int32を使用すると、値の範囲は大きくなりますが、比較的制限されます。個人的には、実際には値の不足を心配する必要がないように、Idにbigint列を頻繁に使用します。


6

他の人々がインクリメントする主キーを主張しているように、私はGUIDの1つを作成します:

  • 一意であることが保証されています
  • アプリケーションのデータを取得するために、データベースへのアクセスを1回減らすことができます。(たとえば、タイプテーブルの場合、GUIDをアプリケーションに保存し、それを使用してレコードを取得できます。IDを使用する場合は、名前でデータベースをクエリする必要があり、これを実行してPKを取得する多くのアプリケーションを見てきました後で再度クエリを実行して詳細を取得します)。
  • データを隠すのに便利です。www.domain.com/Article/2では2つの記事しかありませんが、www.domain.com / article / b08a91c5-67fc-449f-8a50-ffdf2403444aでは何もわかりません。
  • 異なるデータベースのレコードを簡単にマージできます。
  • MSFTは、IDにGUIDSを使用します。

編集:ポイントの複製


5
-1。GUID / UUIDは一意であることが保証されておらず、100%一意ではありません。GUIDは依然として有限の長さであるため、ある時点で、重複する可能性は非常に低くなりますが、重複する可能性があります。データベースへのアクセスが少ないという点も無効です。GUIDキーのように、アプリケーションにプライマリIDを保存できないのはなぜですか?
ニクラスH

2
ジェフ・アトウッドは、私が今までできたよりもずっと良いと言います。 blog.codinghorror.com/primary-keys-ids-versus-guids
スリーバリューロジック

なぜアプリケーションにプライマリIDを保存できないのですか?データベースが作成するためです。空のデータベースでシードを実行する場合、IDは1であると想定できます。データが含まれるデータベースで同じスクリプトを実行するとどうなりますか?IDは1ではありません
三値論理

アプリケーションでIDを作成することについて何も言わなかった-「保存」を書いただけです。ただし、データベースの外部でIDを作成する必要がある場合は、はい、GUIDが答えになる可能性があります。
ニクラスH

2
私は彼らがより良いスケールを追加します。CassandraのようなビッグデータNoSQLデータベースは、自動インクリメントキーもサポートしていません。
カールビーレフェルト

2

優れた設計の原則として、すべてのテーブルには、行を一意に識別する信頼できる方法が必要です。それが主キーの目的ですが、主キーの存在が常に必要なわけではありません。主キーをすべてのテーブルに追加することは、一意の行識別を提供するため、悪い習慣ではありませんが、不要な場合があります。

2つ以上のテーブルの行間の信頼できる関係を維持するには、外部キーを介して行う必要があります。したがって、少なくともいくつかのテーブルで主キーが必要になります。すべてのテーブルに主キーを追加すると、新しいテーブルまたはリレーションシップを既存のデータに追加するときに、データベース設計を簡単に拡張できます。事前の計画は常に良いことです。

基本原則(おそらくハードルール)として、主キーの値は、その行の存続期間を通じて決して変更されるべきではありません。行内のビジネスデータはその存続期間中に変更される可能性があると想定するのが賢明です。そのため、ビジネスデータは主キーの適切な候補ではありません。これが、自動インクリメント整数のような抽象的な何かがしばしば良いアイデアである理由です。ただし、自動インクリメントされた整数には制限があります。

データがデータベース内でのみ有効である場合、自動インクリメントされた整数で十分です。しかし、他の回答で述べたように、データの共有、同期、またはデータベース外での生活を希望する場合、自動インクリメントされた整数は主キーを貧弱にします。より良い選択は、guid(別名uuid "universally unique id")です。


2

質問と回答の多くは、各テーブルのすべての自然キーがデータベースの論理スキーマにのみ存在し、各テーブルのすべての代理キーがデータベースの物理スキーマにのみ存在するという重要なポイントを見逃しています。他の回答では、サロゲートキーが適切に使用される理由とタイミングを説明せずに、整数とGUIDサロゲートキーの相対的な利点のみを説明します。

BTW:不明確で不正確な用語の主キーの使用を避けましょう。これは、リレーショナルモデルに最初に(無意味に)採用され、その後さまざまなRDBMSベンダーによって物理ドメインに再度採用された、リレーショナル前データモデルの成果物です。その使用は、セマンティクスを混乱させるだけです。

リレーショナルモデルから、データベースの論理スキーマ最初の標準形式にするために、すべてのテーブルには、テーブルの各行を一意に識別する、ユーザーが表示できるナチュラルキーと呼ばれるフィールドのセットが必要であることに注意してください。ほとんどの場合、そのような自然なキーは容易に識別されますが、タイブレーカーフィールドとして、またはその他の方法で、キーを構築する必要がある場合があります。ただし、このような構築されたキーは常にユーザーに表示されるため、常にデータベースの論理スキーマに存在します。

対照的に、テーブルの代理キーはデータベースの物理スキーマに純粋に存在します(したがって、セキュリティ上の理由とデータベースの整合性の維持の両方のために、データベースユーザーには常に完全に見えないようにする必要があります)。代理キーを導入する唯一の理由は、DBの物理的なメンテナンスと使用におけるパフォーマンスの問題に対処することです。結合、複製、データ用の複数のハードウェアソース、その他のいずれかです。

代理キーを導入する唯一の理由はパフォーマンスであるため、パフォーマンスを向上させたいと考えています。目前のパフォーマンスの問題が結合である場合は、サロゲートキーをできる限り狭くする必要があります(ハードウェアを邪魔することなく、通常、短い整数とバイトは出力されません)。結合のパフォーマンスは最小のインデックスの高さに依存するため、4バイト整数は自然な解決策です。パフォーマンスの問題が挿入率である場合、4バイト整数も自然な解決策になる可能性があります(RDBMSの内部によって異なります)。テーブルのパフォーマンスの問題がレプリケーションまたは他のサロゲートキーテクノロジーよりも複数のデータソースである場合、GUIDまたは2つの部分からなるキー(ホストID +整数)の方が適している可能性があります。私は個人的にGUIDのお気に入りではありませんが、便利です。

要約すると、すべてのテーブルが(任意のタイプの)代理キーを必要とするわけではありません。これらは、検討中のテーブルのパフォーマンスに必要と考えられる場合にのみ使用してください。どの一般的な代理キーテクノロジーを選択するかに関係なく、選択を行う前にテーブルの実際のニーズについて慎重に検討してください。テーブルのサロゲートキーテクノロジーの選択を変更すると、手間がかかります。テーブルの主要なパフォーマンスメトリックを文書化して、後継者が選択内容を理解できるようにします。

特殊なケース

  1. ビジネス要件が監査(またはその他)の目的でトランザクションの連番を要求する場合、そのフィールドは代理キーではありません。それは自然なキーです(追加要件があります)。ドキュメントから、自動インクリメント整数はサロゲートキーのみを生成するので、それを生成する別のメカニズムを見つけてください。当然、何らかのモニターが必要になります。トランザクションを複数のサイトから調達している場合、モニターの指定ホストサイトであるため、1つのサイトが特別になります。

  2. テーブルが約100行を超えない場合、インデックスの高さは関係ありません。すべてのアクセスはテーブルスキャンによるものです。ただし、長い文字列での文字列比較は、4バイト整数の比較よりもはるかに高く、GUIDの比較よりも高くなります。

  3. char(4)コードフィールドでキー付けされたコード値のテーブルは、4バイト整数を持つものと同じようにパフォーマンスが高くなければなりません。私はこれの証拠を持っていませんが、私は頻繁に仮定を使用し、それを破る理由がありませんでした。


-1

良い習慣ではないだけでなく、実際には、ビル・カーウィンのSQL Antipatterns本でアンチパターンとして説明されています。

すべてのテーブルが擬似キー(モデルのセマンティックな値を持つものではなく、任意の値を持つ主キー)を必要とするわけではなく、常に呼び出す理由はありませんid


ポイントが作られ、前9つの回答で説明した上で、これはかなりのものを提供していないようだ
ブヨ

2
そしてなぜこれが重要なのでしょうか?
ブヨ

3
@gnat質問に直接対処するベストプラクティスに関する本であるため。それは明らかではありませんか?
ペドロヴェルネック

3
わずかではありません。Googleの「SQL予約のベストプラクティス」の検索が私におよそ900Kのリンクを示し、なぜこの1つは特に価値があるだろう
ブヨ

1
@gnat私は一日中議論するつもりはありません。あなたは答えが好きではありません、それがダウン投票の目的です。
ペドロウェルネック

-2

これはかなり普遍的です-そうでなければ、キーが実際に一意であることを検証する必要があります。これは、他のすべてのキーを調べることによって行われます...時間がかかります。レコード番号がキーオーバーフロー値に近づくにつれて、増分キーを使用するとコストがかかります。

私は通常、ポインタをより明確なフィールド名ref_{table}または類似のアイデアにします。

外部的にレコードを指す必要がない場合は、IDは必要ありません。


キーロールオーバー値?
AJJ

符号なし整数の最大値は4294967295で、1を追加すると0にロールオーバーされます。レコードを追加してから削除しても、カウンターは増加します。必ずunsigned intフィールドタイプに使用してください。そうしないと、制限はその数の半分になります。
ジョニーV


2
多数の行を追加/削除すると、最終的に自動インクリメントカウンターがオーバーフローします。
ジョニーV

1
人々はどのようにロールオーバーを処理しますか?削除されない低いIDのレコードがあり、一部のIDが4294967295の上端にある終わり近くに向かっている場合はどうなりますか?「インデックスの再作成」を実行できますか?
AJJ

-2

いつもやるべきだとは言いません。ここには一意のキーのないテーブルがありますが、それは必要ありません。監査ログです。更新は決して行われず、クエリはログに記録されているものに対するすべての変更を返しますが、それは合理的な方法で行うことができる最良のものであり、人間が不法な変更を定義する必要があります。(コードができれば、そもそもそれを許可していなかったでしょう!)


-3

主キーの自動インクリメントカウンターはお勧めできません。これは、データを挿入する前に、データベースに戻って次のキーを見つけ、1ずつ増やす必要があるためです。

そうは言っても、データベースが主キーに提供できるものであれば、アプリケーションの一部としてではなく、一般的に使用します。

データベースにネイティブに提供させることにより、キーが必要なものに対して一意であることを保証できます。

もちろん、すべてのデータベースがサポートしているわけではありません。その場合、私は通常、キーバケットを格納するテーブルを使用し、アプリケーションで管理される高範囲と低範囲を使用します。これは、10000の範囲の番号を取得し、アプリケーションインスタンスで自動的にインクリメントするため、私が見つける最もパフォーマンスの高いソリューションです。別のアプリケーションインスタンスは、処理する別の数のバケットを選択できます。64ビット長など、十分に大きなプライマリキープリミティブが必要です。

UUIDを主キーとして使用しないのは、それらを構築して格納するコストが、長い値を1ずつ増やすよりもはるかに高いためです。UUIDは、理論的には重複が発生する可能性があるという点で、誕生日のパラドックスを扱っています。


3
いいえ。自動増分キーは、キーの増分がデータベースによって自動的に行われることを意味します。時々(私はあなたを見ています、オラクル!)そのためにはシーケンスとトリガーの組み合わせが必要ですが、キーに以前に挿入された値を検索し、1を追加して使用する必要ありませ
SQB

JPAなどの一部の永続フレームワークでは、作成されたキーの値を呼び出し元に返す場合、キーを表示するにはレコードをロードする必要があります。
アルキメデス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.