web-dev-qa-db-ja.com

PKの目的でのみ、自動インクリメント/ IDENTITYフィールドを相互参照テーブルに追加する必要がありますか?

次の相互参照テーブルをSQL ServerがホストするDBに追加します。

_company_id bigint not null (FK)
org_path nvarchar (2048) not null
_

_company_id_フィールドは、別のテーブル(主キーである)のidフィールドを参照します。

同じ_company_id_を持つ複数のレコードが存在する可能性がある場合、すべての主キーで両方のフィールドを使用する必要があります。ただし、SQL Serverに対して_org_path_が長すぎるため、両方のフィールドを使用してキーを作成できません。

_org_path_については、これが存在する唯一のテーブルです。このテーブルへのクエリがallエントリ、または_org_path_によるすべての_company_id_エントリのいずれかを要求する可能性はすべてあります。または別の言い方をすると、このテーブルが_org_path_によってクエリされることは疑わしいようです。さらに、_org_path_が更新される可能性は低く、挿入される可能性が高く、ほとんど削除されません。

行の総数は数千になると思います。

また、それがnvarchar (2048)である理由は、値がサードパーティのDBの値を模倣する必要があるためです。典型的な例は次のようになります

_\Translation Providers\[customer name]\[order name]\
_

分音符号を含めることができます。

だから私の質問はこれです:自動インクリメントidフィールドを追加し、それを_company_id_と組み合わせて主キーとして使用する方が効率的ですか、それとも不必要なオーバーヘッドを追加しますか? _company_id_が別のテーブルの主キーであるという事実は、ここで何か影響がありますか?

9
awj

_comany_id_のみの一意でないクラスター化インデックスの場合、SQL Serverはすべての重複する(つまり、キー値の2番目以降)クラスター化インデックスキーに4バイトの整数の一意名を自動的に追加して、一意にします。ただし、これはユーザーには公開されません。

独自の一意の識別子を二次キー列として追加する利点は、_company_id_でシークできるが、個々の行をより効率的にシークできることです(_company_id, identitycol_ではなく_company_id_を使用します)。 _org_path_)の残余述語。その後、クラスタ化インデックスは_company_id, identitycol_で一意になるため、非表示の一意識別子は追加されません。

また、_(company_id,org_path)_の重複が発生する場合は、明示的なID列(一種の「公開された一意名」)があると、削除または更新の対象を1つだけに簡単に指定できます。

7
Martin Smith

考慮すべき1つのことは、主キーとクラスター化インデックスは同じものではないということです。主キーは制約であり、データが存続するルール(つまり、データの整合性)を扱います。効率/パフォーマンスとは関係ありません。主キーでは、キー列が(組み合わせて)一意であり、(個別に)NOT NULLである必要があります。 PKは一意のインデックスを介して適用されますが、クラスター化または非クラスター化することができます。

クラスタ化インデックスは、テーブル内のデータを物理的に(つまり、ディスク上で)並べ替え、パフォーマンスを処理する手段です。データの整合性とは関係ありません。クラスタ化インデックス can では、キー列が一意に(組み合わせて)必要ですが、そうである必要はありません。ただし、クラスタ化インデックスはデータの物理的な順序であるため、何であれ各行を一意に識別する必要があります。したがって、一意性を要求するように設定しない場合、非表示の4バイトの「一意化」列を介して独自の一意性が作成されます。その列は常に非一意のクラスター化インデックスに存在しますが、キーフィールドが(組み合わせて)一意である場合、領域を占有しません。この「一意化」列がどのように機能するか(クラスター化インデックスおよび非クラスター化インデックスへの影響の両方)を直接確認するには、Pastebinに投稿した次のテストスクリプトを確認してください。 一意化サイズをテストするT-SQLスクリプト

したがって、主な質問:

自動インクリメントidフィールドを追加し、それを_company_id_と組み合わせて主キーとして使用する方が効率的ですか、それとも不要なオーバーヘッドが追加されますか

は、これら2つの概念を融合しているため、明確に重複している部分もありますが、個別に対処する必要があります。

IDENTITY列を追加する必要がありますか、それとも不要なオーバーヘッドですか?

_INT IDENTITY_列を追加し、それを使用してPKを作成すると、クラスター化されたPKであると想定して、すべての行に4バイトが追加されます。この列は表示され、クエリで使用できます。 could は、外部キーとして他のテーブルに追加できますが、この特定のケースでは発生しません。

_INT IDENTITY_列を追加しないと、このテーブルにPKを作成できません。ただし、UNIQUEオプションを使用しない限り、テーブルにクラスター化インデックスを作成できます。この場合、SQL Serverは、「uniquifier」と呼ばれる非表示の列を追加します。これは、上記のように動作します。列は非表示であるため、クエリで使用したり、外部キーの参照として使用したりすることはできません。

したがって、効率に関する限り、これらのオプションはほぼ同じです。はい、一部の行(最初の一意のキー値を持つ行)が0バイトを占め、すべての行が中にあるため、非一意のクラスター化インデックスを使用することで、少し少ないスペースが使用されますIDENTITY/PKは4バイトを使用します。ただし、0バイトの行(特に、予想される少量の行)で違いに気づくのに十分ではなく、クエリでID列を使用できるという利便性を上回ることは言うまでもありません。

INT IDENTITY列または_org_path_のハッシュ永続化計算列?

_org_path_の値に基づいて行を検索しないことを考えると、永続的な計算列のオーバーヘッドを追加することは意味がありません。さらに、計算結果と照合するためにクエリでハッシュを計算する必要があります。列(これは私の最初の提案であり、変更履歴 here で利用可能でした。これは最初の文言/質問の詳細に基づいていました)。この特定のケースでは、_INT IDENTITY_ "ID"列がおそらく最適です。

キー列の順序

ID列がクエリで使用されることはほとんどありませんが、およびの2つの主な使用例が「すべての行」または「指定された_company_id_のすべての行」、私は_company_id, id_にPKを作成します。これは、行が順番に挿入されないことを意味するため、FILLFACTORを90に指定します。断片化を減らすために、定期的なインデックスメンテナンスを必ず行う必要もあります。

2番目の質問

company_idが別のテーブルの主キーであるという事実は、ここで効果がありますか

番号。

トリガー

_org_path_内の_company_id_値は一意であるため、これを強制するには_INSERT, UPDATE_にトリガーを作成する必要があります。トリガーで、おそらくCOUNT(*)および_IF EXISTS_を実行するクエリで_GROUP BY company_id, org_path_を実行します。何かが見つかった場合は、ROLLBACKを発行してDML操作をキャンセルし、RAISERRORを発行して重複があることを伝えます。

照合

私の最初の答え(元の文言/質問のまばらな詳細に基づいており、改訂履歴 ここ で利用可能)で、バイナリ(つまり__BIN2_)照合を使用することを提案しました。 _org_path_が正確に何であるかについての洞察を得たので、バイナリ照合を使用することをお勧めします /。分音記号があるので、 do は、言語学的同等性を利用したいと考えています。

12
Solomon Rutzky

なぜPKが必要なのですか?

非クラスター化インデックスとしてcompany_idを使用しないのはなぜですか?

最も検索されたのはすべてのエントリか、company_idでした
めったに更新しない
めったに削除しない
org_path、これが存在する唯一のテーブル

マーティン・スミスからの回答で、必要なものが手に入るかもしれません
4バイト整数の一意名を自動的に追加することに慣れていません
何か不足している可能性がありますが、他の列にインデックスを付けていない場合、この使用例ではその目的はわかりません

DRIが心配な場合、テーブルはCompanyテーブルをcompany_idのFKとして使用する必要があります。

0
paparazzo