私が取り組んでいる1つのWebアプリケーション内では、すべてのデータベース操作は、Entity Framework ORMで定義されたいくつかの一般的なリポジトリを使用して抽象化されています。
ただし、汎用リポジトリーのシンプルな設計を実現するには、関連するすべてのテーブルで一意の整数(C#では_Int32
_、SQLではint
)を定義する必要があります。これまで、これは常にテーブルのPKであり、IDENTITY
でもありました。
外部キーは頻繁に使用され、これらの整数列を参照します。これらは、ORMによる一貫性とナビゲーションプロパティの生成の両方に必要です。
アプリケーション層は通常、次の操作を実行します。
SELECT * FROM table
_UPDATE table SET Col1 = Val1 WHERE Id = IdVal
_DELETE FROM table WHERE Id = IdVal
_INSERT INTO table (cols) VALUES (...)
頻度の低い操作:
BULK INSERT ... into table
_の後に(*)すべてのデータロードが続く(生成された識別子を取得するため)DELETE FROM table where OtherThanIdCol = SomeValue
_UPDATE table SET SomeCol = SomeVal WHERE OtherThanIdCol = OtherValue
_*すべての小さなテーブルはアプリケーションレベルでキャッシュされ、ほとんどすべてのSELECTs
はデータベースに到達しません。典型的なパターンは、初期ロードと多数のINSERT
s、UPDATE
sおよびDELETE
sです。
現在のアプリケーションの使用状況に基づいて、テーブルのいずれかで1億レコードに達する可能性はごくわずかです。
質問:DBAの観点から、このテーブルの設計に制限があると、重大な問題が発生する可能性がありますか?
[編集]
回答(素晴らしいフィードバックに感謝)と参照記事を読んだ後、さらに詳細を追加する必要があるように感じます。
現在のアプリケーションの詳細-他のアプリケーションでもモデルを再利用できるかどうかを知りたいので、現在のWebアプリケーションについては触れませんでした。ただし、私の特定のケースは、DWHから大量のメタデータを抽出するアプリケーションです。ソースデータは非常に乱雑で(奇妙な方法で非正規化されており、不整合があり、多くの場合自然な識別子がないなど)、私のアプリは明確に分離されたエンティティを生成しています。また、生成された識別子(IDENTITY
)の多くが表示されるので、ユーザーはそれらをビジネスキーとして使用できます。 これは、大規模なコードリファクタリングに加えて、GUIDの使用を除外します。
"行を一意に識別する唯一の方法であってはなりません"(Aaron Bertrand♦)-これは非常に良いアドバイスです。また、すべてのテーブルでUNIQUE CONSTRAINTを定義して、ビジネスの重複が許可されないようにしています。
フロントエンドのアプリ駆動型設計とデータベース駆動型設計-設計の選択はこれらの要因によって引き起こされます
Entity Frameworkの制限-複数の列PKが許可されますが、 それらの値は更新できません
カスタム制限-単一の整数キーを持つことで、データ構造と非SQLコードが大幅に簡素化されます。例:すべての値のリストには、整数キーと表示された値があります。さらに重要なのは、キャッシュ用にマークされたテーブルが_Unique int key -> value
_マップに入れられることを保証することです。
複雑な選択クエリ-すべての小さな(<20-30Kレコード)テーブルデータはアプリケーションレベルでキャッシュされるため、これはほとんど起こりません。これにより、アプリケーションコードを作成するときの生活が少し難しくなります(LINQを書くのが難しくなります)が、データベースのヒット率は大幅に向上します。
List views-ロード時にSELECT
クエリが生成されません(すべてがキャッシュされます)または次のようなクエリ:
_SELECT allcolumns FROM BigTable WHERE filter1 IN (val1, val2) AND filter2 IN (val11, val12)
_
他のすべての必要な値はキャッシュルックアップ(O(1))を通じてフェッチされるため、複雑なクエリは生成されません。
Edit views-次のようなSELECT
ステートメントを生成します:
_SELECT allcolumns FROM BigTable WHERE PKId = value1
_
(すべてのフィルターと値はint
sです)
追加のディスク領域(およびメモリ使用量とI/O)を除いて、IDENTITY列を追加する必要がないテーブルにも、 harm を追加しても実際にはありません(例IDENTITY列を必要としないテーブルの例は、ユーザーを自分の権限にマッピングするような単純なジャンクションテーブルです)。
2010年のブログ投稿の every single table に盲目的にそれらを追加することに対して私はレールを張っています:
ただし、サロゲートキーには有効な使用例があります。一意性が保証されていると想定しないように注意してください(これが追加される理由となる場合があります。これらはであってはなりませんのみ行を一意に識別する方法)。 ORMフレームワークを使用する必要があり、実際のキーが整数でないか、単一の列ではない、またはどちらでもない場合でも、ORMフレームワークで単一列の整数キーが必要な場合は、一意の制約/インデックスを定義してください。あなたの本当の鍵にも。
私の経験から、すべてのテーブルに個別のIDを使用する主で圧倒的な理由は次のとおりです。
ほとんどすべての場合、私の顧客は構想段階で血の誓いを誓いました。外部の「自然な」フィールドXYZBLARGH_ID
は永久に一意のままであり、また、特定のエンティティに対して変更されることはなく、再利用されることもありません。主キープロパティが壊れるケースが最終的に発生しました。それだけではうまくいきません。
次に、DBAの観点から見ると、DBが遅くなったり膨れたりする原因は、行ごとに4バイト(または何でも)ではなく、インデックスの誤りや欠落、テーブル/インデックスの再編成の忘れ、RAM /テーブルスペースのチューニングパラメータの誤りなどです。 、バインド変数の使用を無視するなど。 Thoseは、追加のID列ではなく、10、100、10000 ...の係数でDBを遅くする可能性があります。
したがって、行ごとに32ビットを追加するという技術的で測定可能なマイナス面があったとしても、IDを最適化できるかどうかではなく、IDが必須になるかどうかは問題ではありませんある時点で、そうなる可能性が高くなります。そして、私はソフトウェア開発スタンスからのすべての「ソフト」の利点(ORMの例、または設計によりすべてのIDが同じデータ型を持つ場合にソフトウェア開発者がより簡単になるという事実など)を数えるつもりはありません。 。
注:n:m
関連付けテーブルに個別のIDは必要ありません。そのようなテーブルでは、関連付けられたエンティティのIDが主キーを形成する必要があるためです。反例は、奇妙なn:m
アソシエーションであり、奇妙な理由で同じ2つのエンティティ間の複数のアソシエーションを許可します-それらは独自のIDを必要とします次に、PKを作成します。ただし、複数列のPKを処理できないORMライブラリーがあるので、開発者が作業する必要がある場合、それが開発者に寛大になる理由になります。そのようなライブラリ。
すべてのテーブルに常に意味のない追加の列を追加し、それらの列のみを外部キーとして参照すると、ほぼ必然的にデータベースがより複雑になり、使用が難しくなります。事実上、ユーザーが関心のあるデータを外部キー属性から削除し、ユーザー/アプリケーションに追加の結合を行わせて、同じ情報を取得します。クエリはより複雑になり、オプティマイザの仕事が難しくなり、パフォーマンスが低下する可能性があります。
テーブルには、本来のデータよりも「実際の」データがまばらに表示されます。したがって、データベースの理解と検証はさらに困難になります。また、特定の有用な制約を強制するのが困難または不可能な場合もあります(制約には、同じテーブルに存在しない複数の属性が関係します)。
十分な理由がある場合にのみ、より慎重にキーを選択し、整数にすることをお勧めします。独断的なルールに頼るのではなく、優れた分析、データの整合性、実用性、検証可能な結果に基づいてデータベースの設計を行ってください。
さまざまなデータベースでの私の経験では、整数主キーは、キーがまったく定義されていないアプリケーションより常に優れています。または、半ダースのvarchar列をぎこちなく結合するキーを持つアプリケーション論理的でない方法...(ため息)
整数PKからGUIDに切り替えたアプリケーションを見てきました。そうした理由は、特定のケースで複数のソースデータベースのデータをマージする必要があったためです。開発者はallのキーをGUIDに切り替えたので、マージの一部ではなかったテーブル(これらのテーブルの場合に備えて)でも、データの衝突を心配することなくマージを実行できます将来のマージの一部になりました)。
個別のソースからのデータをマージすることを計画していないか、整数サイズの制限を超えるデータがある可能性がない限り、整数PKはあなたに噛みつくことはないと思います-挿入のためのスペースがなくなるまで、それはすべて楽しいゲームです。
ただし、テーブルがより頻繁にクエリされる場合は、クラスターインデックスをPK以外の列に設定することができると言えます。しかし、特に更新と選択の大部分がPK値に基づいている場合は、これは優れたケースです。
脇に置く:
必要に応じて一括削除/一括更新を使用しており、そのような操作をサポートするインデックスがある場合、使用するPK標準が原因で問題が発生することはないと思います。
後でEFに結合などのクエリを生成させると、自然なキーベースのリポジトリの場合ほど効率が悪くなる可能性がありますが、その領域については十分に理解できていません確かにどちらの方法でも。
あなたを導くのに役立ついくつかの要因があります、
定義と仕様
タスクまたは物理法則によって何かが一意であると定義されている場合、代理キーで時間を浪費しています。
一意性
個人の健全性、結合、およびより高レベルのデータベース機能には、(a)一意の列、(b)一連の一意の列のいずれかが必要です。
すべての十分に正規化されたスキーマ(1NF)は、次のいずれかを提供します。そうでない場合は常に作成する必要があります。あなたが日曜日にボランティア活動を開始するように設定されている名簿があり、姓と名が含まれている場合は、2人のジョーボブがいることを知りたいと思うでしょう。
実装と最適化
Intは、比較と同等性のために高速な小さなデータ形式になる傾向があります。これを、照合順序がロケール(場所と言語)に依存する可能性があるUnicode文字列と比較してください。 4242をASCII/UTF8文字列に格納するのは4バイトです。 2バイトに収まる整数として格納します。
したがって、マイナス面に関しては、いくつかの要因があります。
混乱と曖昧さ
スペース。
整数は行にスペースを追加します。そして、それらを使用していない場合、目的はありません。
クラスタリング
データは一方向にしか注文できません。不要な代理キーを課す場合、その方法でクラスタ化するのか、それとも自然キーの方法で行うのか?