私のデータベースでは、特定の行を一意に検索できるように、作成するすべてのテーブルにid
という名前の自動インクリメント整数主キーを使用する傾向があります。
これは悪い考えだと思いますか?この方法でそれを行うことに欠点はありますか? id, profile_id, subscriptions
のような複数のインデックスが時々あります。ここで、id
は一意の識別子、profile_id
はid
テーブルの外部Profile
へのリンク、等.
または、そのようなフィールドを追加したくないシナリオはありますか?
一意の行識別子が保証されていることは決して悪い考えではありません。絶対に言ってはいけないことだと思いますが、圧倒的な大部分の時間に行きましょう。
理論的な潜在的な欠点には、維持する追加のインデックスと使用される追加のストレージ領域が含まれます。それを使用しない理由は、これだけでは十分ではありません。
以前はすべての答えに同意しません。すべてのテーブルに自動インクリメントフィールドを追加するのが悪い理由はたくさんあります。
明らかなキーがないテーブルがある場合は、自動インクリメントフィールドを使用することをお勧めします。結局のところ、select * from blog where body = '[10000 character string]'
を使いたくないのです。むしろselect * from blog where id = 42
。これらのほとんどの場合、本当に必要なのは一意の識別子であると私は主張します。連続した一意の識別子ではありません。代わりに、普遍的に一意の識別子を使用することをお勧めします。
ほとんどのデータベースには、ランダムな一意の識別子を生成する関数があります(mysqlではuuid
、mssqlではnewid
)。これらを使用すると、異なるマシン上の複数のデータベースに、ネットワーク接続なしでいつでもデータを生成でき、データを競合なしでマージできます。これにより、複数のサーバーや、たとえばマイクロサービスなどのデータセンターをより簡単にセットアップできます。
これにより、攻撃者がアクセスしてはならないページのURLを推測することも回避されます。 https://example.com/user/1263
がある場合は、おそらくhttps://example.com/user/1262
もあるでしょう。これにより、ユーザープロファイルページのセキュリティエクスプロイトを自動化できます。
また、uuidカラムが役に立たない場合や有害な場合も多くあります。あなたがソーシャルネットワークを持っているとしましょう。 users
テーブルとfriends
テーブルがあります。 friendsテーブルには、2つのuserid列と自動インクリメントフィールドが含まれています。 3
を5
と友達にしたいので、3,5
をデータベースに挿入します。データベースは自動インクリメントIDを追加し、1,3,5
を格納します。どういうわけか、ユーザー3
は[友達を追加]ボタンをもう一度クリックします。 3,5
をデータベースに再度挿入すると、データベースによって自動インクリメントIDが追加され、2,3,5
が挿入されます。しかし、今では3
と5
は2回お互いに友達になりました!これはスペースの浪費です。考えてみれば、自動インクリメントカラムも同様です。 a
とb
が友だちかどうかを確認するには、これら2つの値を持つ行を選択するだけです。これらは一緒に、一意の行識別子です。 (おそらく、3,5
と5,3
が重複排除されるようにするためのロジックを記述したいと思うでしょう。)
Url-shortenerを作成するときなど、シーケンシャルIDが役立つケースはまだありますが、ほとんどの場合(URLショートナーを使用しても)、ランダムに生成された一意のIDが実際に代わりに使用したいものです。
TL; DR:各行を識別する一意の方法がない場合は、自動インクリメントではなくUUIDを使用します。
自動インクリメンタルキーには主に利点があります。
しかし、考えられるいくつかの欠点は次のとおりです。
Wikipediaの記事のセクションはこちら 代理キーの欠点について。
逆に言えば、いいえ。常にAutoInc PKの数値を持っている必要はありません。
データを注意深く分析すると、データ内の自然なキーを特定することがよくあります。これは、データがビジネスにとって本質的な意味を持つ場合によく見られます。 PKは、ビジネスユーザーがシステムの属性を説明するための第2言語として使用する、古代のシステムのアーティファクトである場合があります。たとえば、車両管理システムで「車両」テーブルの主キーとして使用される車両のVIN番号を見てきました。
ただし、それが作成されたものであり、すでに一意の識別子がある場合は、それを使用してください。意味のない2番目の主キーを作成しないでください。それは無駄であり、エラーを引き起こす可能性があります。
AutoInc PKを使用して、顧客にとって意味のある値を生成できる場合があります。ポリシー番号。開始値を適切な値に設定し、先行ゼロなどに関するビジネスルールを適用します。これは、おそらく「両方の長所」のアプローチです。
比較的静的な値が少ない場合は、システムユーザーにとって意味のある値を使用してください。 L、C、Hを使用できる場合に1,2,3を使用する理由L、H、Cは保険の「保険契約タイプ」のコンテキストで生命、自動車、住宅を表すか、またはVINの例に戻って、「TO 「トヨタにとって?すべてのToyata車には、「TO」で始まるVINがあります。これは、ユーザーが覚えておかなければならないことが1つ少なくなり、プログラミングエラーやユーザーエラーが発生する可能性が低くなり、管理レポートの完全な説明の代用にもなり、レポートが簡単になります書くこと、そしておそらく生成するのがより速い。
これのさらなる開発はおそらく「橋が遠すぎる」であり、私は一般にそれをお勧めしませんが、完全にするためにそれを含めて、あなたはそれの良い使用法を見つけるかもしれません。つまり、説明を主キーとして使用します。急速に変化するデータにとって、これは嫌悪です。 veryAll The Timeでレポートされる静的データの場合、そうではない可能性があります。言及するだけで、可能性としてそこに座っています。
私はAutoInc PKを使用しています。頭を使って、より良い代替案を最初に探します。データベース設計の芸術は、迅速に照会できる意味のあるものを作っています。結合が多すぎると、これが妨げられます。
編集自動生成されたPKが不要なもう1つの重要なケースは、他の2つのテーブルの共通部分を表すテーブルの場合です。車のアナロジーに固執するために、車には0..nのアクセサリーがあり、各アクセサリーは多くの車にあります。これを表すには、CarおよびAccessoryのPKと、日付などのリンクに関するその他の関連情報を含むCar_Accessoryテーブルを作成します。
(通常)必要としないものは、このテーブルのAutoInc PKです。これは、車からのみアクセスできます。「この車に付属しているアクセサリーを教えて」または「この車のアクセサリーを教えて」
多くのテーブルにはすでに固有のIDがあります。これらのテーブルに別の一意のID列(自動インクリメントなど)を追加しないでください。代わりに、自然な一意のIDを使用してください。別の一意のIDを追加すると、基本的にデータに冗長性(重複または依存関係)が生じます。これは正規化の原則に反します。 1つの一意のIDは、正確さのために他のIDに依存しています。これは、これらの行を管理するすべてのシステムで常時で完全に同期している必要があることを意味します。これは、長期にわたって管理および検証する必要が本当にない、データ整合性のもう1つの脆弱性です。
最近のほとんどのテーブルでは、一意のid列を追加することによるパフォーマンスのわずかな向上は実際には必要ありません(場合によっては、パフォーマンスが低下することもあります)。 ITの一般的なルールとして、plague!のような冗長性は避けてください。それは拒絶反応です。そして、引用に注意してください。すべては可能な限り単純でなければなりませんが、単純ではありません。自然なものが整然としていないように見えても、1つで十分な2つの一意のIDは使用しないでください。
すべてのルールには例外があるため、エクスポート/インポートに使用されるステージングテーブル、および同様の一方向テーブルまたは一時テーブルでは、整数の自動インクリメントIDは必要ない場合があります。分散システムでは、IDではなくGUIDを使用することもできます。
ここでの多くの回答は、既存の一意のキーを取得する必要があることを示唆しています。まあ150文字でも?私はそうは思いません。
今私の主なポイント:
自動インクリメント整数IDの反対者は、最大20のテーブルを持つ小さなデータベースについて話しているようです。そこでは、各テーブルに個別のアプローチをする余裕があります。
ただし、ERP 400以上のテーブルで、任意の場所に整数の自動インクリメントIDがある場合(上記の場合を除く)ちょうど意味があります。他の一意のフィールドが存在し、一意性が確保されている場合でも、それらに依存することはありません。
JOIN
します。大規模なシステムでは、これらの個々の主キーの小さな利点を無視して、ほとんどの場合、整数の自動インクリメントIDを一貫して使用する価値があります。既存の一意のフィールドを主キーとして使用すると、レコードあたり数バイトを節約できる可能性がありますが、今日のデータベースエンジンでは、追加のストレージまたはインデックス作成時間は問題ありません。 実際には、開発者/メンテナの無駄な時間で、はるかに多くのお金とリソースを失っています。今日のソフトウェアは、プログラマーの時間と労力に合わせて最適化する必要があります。一貫したIDを使用したアプローチは、はるかに優れています。
余分なデザインをすることは良い習慣ではありません。つまり-必要のないときに常に自動インクリメントの主キーを保持することはお勧めしません。
不要な例を見てみましょう。
記事のテーブルがあります。これには、int主キーid
、およびtitle
という名前のvarchar列があります。
また、記事のカテゴリの完全なテーブル–id
int主キー、varchar name
もあります。
Articlesテーブルの1行にはid
5とtitle
"バターでガチョウを調理する方法"があります。その記事を、Categoriesテーブルの次の行にリンクしたいとします。 "Fowl"(id:20)、 "Goose"(id:12)、 "Cooking" (id:2)、「バター」(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つの中央データベースがない場合、またはアプリケーションがデータレコードをオフラインで作成できる(後でデータベースにアップロードできる)場合に便利です。
結局のところ、主キーを1つのものとして考える必要はありません。それらを、それらが実行する機能と考える必要があります。なぜ主キーが必要なのですか?将来変更されないフィールドを使用して、テーブルから特定のデータセットを一意に識別できるようにするため。これを行うにはid
という特定の列が必要ですか、またはこの一意の識別を他の(不変の)データに基づいて行うことができますか?
自動インクリメント(ID)主キーは、データベースのコンテキストとそのデータベースの直接のクライアントの外では意味がないことに注意することを除いて、良い考えです。たとえば、一部のデータを別のデータベースに転送して保存し、その後、両方のデータベーステーブルに異なるデータを書き込む場合、IDは分岐します。つまり、1つのデータベースで42のIDを持つデータは、必ずしもデータと一致しませんもう一方のIDは42です。
このため、データベースの外部で行を一意に識別できるようにする必要がある場合(頻繁にそうである場合)、この目的のために別のキーが必要です。慎重に選択したビジネスキーで十分ですが、多くの場合、一意性を保証するために必要な列の数が多くなります。別の手法は、自動インクリメントのクラスター化された主キーとしてId列を持ち、クラスター化されていない一意のキーとして別のuniqueidentifier(guid)列を持つことです。この場合でも自動インクリメントキーが存在する理由は、自動インクリメントキーをクラスタ化してインデックスを作成する方が、GUIDに対して同じことを行うよりも効率的だからです。
自動インクリメントキーが不要な1つのケースは、主キーが他の2つのテーブルのId列の複合である多対多のテーブルです(ここでも自動インクリメントキーを使用できますが、それの要点がわからない)。
もう1つの質問は、自動インクリメントされたキーのデータ型です。 Int32を使用すると、値の範囲は大きくなりますが、比較的制限されます。個人的には、値が不足することを心配する必要がないように、idにbigint列を頻繁に使用しています。
または、そのようなフィールドを追加したくないシナリオはありますか?
承知しました。
まず第一に、自動インクリメントを持たないデータベースがあります(たとえば、Oracleは確かに最も小さな候補の1つではありません)。これは、誰もがそれらを好きまたは必要とするわけではないという最初の兆候であるはずです。
さらに重要なのは、IDが実際に何であるかを考えることですis-これはデータの主キーです。異なる主キーを持つテーブルがある場合、IDは不要であり、IDは必要ありません。たとえば、テーブル(EMPLOYEE_ID, TEAM_ID)
(各従業員が同時に複数のチームに所属できる場合)には、これら2つのIDで構成される主キーが明確に定義されています。このテーブルの主キーでもあるautoincrement ID
列を追加しても、まったく意味がありません。今、あなたは2つの主キーを持ち歩き、「主キー」の最初のWordはあなたが本当に1つしか持つべきではないというヒントを与えます。
私は通常、「長寿命」データ(一度挿入すると予想されるレコード)に新しいテーブルを定義するときに、「ID」列(自動インクリメント整数)を使用して、ビットフィールドを設定することで「論理的に削除」されても無期限に保持します)。
それらを使用したくないときに考えられる状況はいくつかありますが、そのほとんどは、DBの1つのインスタンスの1つのテーブルを新しいID値の信頼できるソースにすることができないシナリオに要約されます。
これらの状況でID列を使用できるようにする回避策はありますが、前述のとおりですが、ほとんどの場合、ID列からGUIDにアップグレードする方が簡単で、問題をより完全に解決します。
他の人がインクリメントする主キーを主張しているので、GUIDの主キーを作成します。
編集:ポイントの複製
質問と多くの回答は、各テーブルのすべての自然キーがデータベースの論理スキーマにのみ存在し、それぞれのすべての代理キーが存在するという重要なポイントを逃していますテーブルは、データベースの物理スキーマにのみ存在します。他の回答では、整数キーとGUID代理キーの相対的な利点のみを取り上げ、代理キーが適切に使用される理由とその時期については触れていません。
ところで:不適切に定義された不正確な用語主キーの使用を避けましょう。これは、最初にリレーショナルモデルに(不適切に)オプトインされ、次にさまざまなRDBMSベンダーによって物理ドメインに再度オプトインされたリレーショナルデータモデルのアーティファクトです。その使用は、セマンティクスを混乱させるためにのみ役立ちます。
リレーショナルモデルから、データベース論理スキーマが最初の正規形になるように=、すべてのテーブルには、テーブルの各行を一意に識別するユーザーに表示されるフィールドのセット、自然キー、が必要です。ほとんどの場合、そのような自然キーはすぐに識別されますが、タイブレーカーフィールドとしてであれ、そうでないものであれ、構築する必要がある場合があります。ただし、このような構築されたキーは常にユーザーに表示されるため、常にデータベースの論理スキーマに存在します。
対照的に、テーブルのサロゲートキーは、データベースの物理スキーマに純粋に存在します(したがって、常にセキュリティの両方のために理由と、データベースの整合性を維持するために、データベースユーザーには完全に見えないようにしてください)。 代理キーを導入する唯一の理由は、DBの物理的なメンテナンスと使用におけるパフォーマンスの問題に対処することです。それらが、結合、レプリケーション、データの複数のハードウェアソース、またはその他のものであるかどうか。
サロゲートキーの導入の唯一の理由はパフォーマンスであるため、パフォーマンスを向上させたいと考えます。パフォーマンスの問題が結合である場合は、代理キーをできるだけ狭くする必要があります(ハードウェアの邪魔にならないように、通常、短い整数とバイトは除外されます)。結合のパフォーマンスは最小のインデックスの高さに依存するため、4バイトの整数が自然な解決策です。パフォーマンスの問題が挿入率である場合は、4バイトの整数も自然な解決策になる場合があります(RDBMSの内部によって異なります)。テーブルのパフォーマンスの問題が、他の代理キーテクノロジよりもレプリケーションまたは複数のデータソースである場合は、GUIDまたは2つの部分からなるキー(ホストID +整数)の方が適しているかもしれませんが、私は個人的にGUIDを好きではありませんが、便利です。
要約すると、すべてのテーブルで代理キー(任意のタイプ)が必要になるわけではありません。これらは、検討中のテーブルのパフォーマンスに必要と思われる場合にのみ使用してください。どの共通代理キーテクノロジを使用するかに関係なく、選択する前にテーブルの実際のニーズについて慎重に検討してください。テーブルのサロゲートキーテクノロジの選択を変更すると、作業が煩雑になります。後継者が行った選択を理解できるように、テーブルの主要なパフォーマンスメトリックを文書化します。
特殊なケース
ビジネス要件が監査(またはその他の)目的でトランザクションの連続番号を義務付けている場合、そのフィールドはnot代理キーです。 自然キーです(追加の要件があります)。ドキュメントから自動インクリメント整数は代理キーのみを生成するため、それを生成する別のメカニズムを見つけます。明らかに、ある種のモニターが必要になります。トランザクションを複数のサイトから調達している場合、指定されているため、1つのサイトはspecialになりますモニターのホストサイト。
テーブルが100行を超えることがない場合、インデックスの高さは関係ありません。すべてのアクセスはテーブルスキャンによって行われます。ただし、長い文字列での文字列比較は、4バイト整数の比較よりもはるかにコストがかかり、GUIDの比較よりもコストがかかります。
char(4)codeフィールドをキーとするcode値のテーブルは、 1つは4バイト整数です。私にはこれの証拠はありませんが、私は仮定を頻繁に使用し、それを否定する理由がありませんでした。
優れた設計の原則として、すべてのテーブルには、行を一意に識別する信頼できる方法が必要です。それが主キーの目的ですが、常に主キーの存在を必要とするわけではありません。すべてのテーブルに主キーを追加することは、一意の行識別を提供するので、悪い習慣ではありませんが、必要ない場合があります。
2つ以上のテーブルの行間の信頼できる関係を維持するには、外部キーを介して行う必要があるため、少なくともいくつかのテーブルに主キーが必要です。すべてのテーブルに主キーを追加すると、新しいテーブルやリレーションシップを既存のデータに追加するときに、データベース設計を拡張しやすくなります。事前の計画は常に良いことです。
基本原則(おそらくハードルール)として、主キーの値はその行の存続期間を通じて変更されるべきではありません。行内のビジネスデータはその存続期間中に変更される可能性があると想定するのが賢明です。そのため、ビジネスデータは主キーの候補としては不十分です。これが、自動インクリメントされた整数のような抽象的なものがしばしば良い考えである理由です。ただし、自動インクリメントされた整数には制限があります。
データがデータベース内でのみ有効である場合、自動インクリメントされた整数で問題ありません。ただし、他の回答で述べたように、データの共有、同期、またはデータベース外でのライフを希望する場合、自動インクリメントされた整数は主キーとして不十分です。より良い選択は、guid(別名uuid "universally unique id")です。