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


9

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

company_id bigint not null (FK)
org_path nvarchar (2048) not null

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

同じを持つ複数のレコードも存在する可能性があることを考えるとcompany_id、主キーは両方のフィールドを使用する必要があります。ただし、org_pathSQL Serverには長すぎるため、両方のフィールドを使用してキーを作成できません。

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

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

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

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

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

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

回答:


7

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

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

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


12

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

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

したがって、主な質問:

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

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

必要がありますIDENTITY列が追加されるか、不要なオーバーヘッドでしょうか?

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

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

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

INT IDENTITY列またはorg_path永続的な計算列のハッシュ?

org_path値に基づいて行を検索しないことを考えると、永続的な計算列のオーバーヘッドに加えて、計算列と照合するためにクエリでそのハッシュを計算する必要があることは意味がありません(これは私の最初の文言/質問の詳細に基づいた、ここの改訂履歴で利用できる元の提案)。この特定のケースでは、INT IDENTITY「ID」列がおそらく最適です。

キー列の順序

ことを考えるとID列はほとんど、これまでの場合は、クエリで使用しない、されると、二つの主なユースケースは、「すべての行」または「特定のためのすべての行のいずれかを取得するためにあることを考えるcompany_id」、私は上のPKを作成しますcompany_id, id。そして、これは行が順番に挿入されないことを意味するので、私はFILLFACTOR90 を指定します。フラグメント化を減らすために、定期的なインデックスメンテナンスを確実に行う必要もあります。

二番目の質問

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

番号。

引き金

以来org_path内の値がcompany_id一意である、あなたはまだ上のトリガーを作成する必要がありINSERT, UPDATE、これを施行します。トリガーでは、やるIF EXISTSだろうし、クエリでCOUNT(*)GROUP BY company_id, org_path。何かが見つかった場合は、a ROLLBACKを発行してDML操作をキャンセルしてから、RAISERROR重複があることを伝えます。

照合

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



0

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

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

ほとんどの検索がすべてのエントリ上にあるか、company_idによって行われたと言いました
まれに更新
まれに
org_pathを削除しました

マーティン・スミスからの回答で、必要なものが得られる かもしれませんが
、4バイト整数の一意名を自動的に追加するのに慣れて
いません。

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


ねえ。「なぜcompany_idを非クラスタ化インデックスとして使用しないのですか?」:2つのマイナス面があるため:1)クラスタ化インデックステーブルなので、追加の項目はなく、もう1つスペースを占有します。 2)INCLUDE列でない限り、NVARCHARフィールドを取得するにはRIDルックアップが必要ですが、テーブルを複製しているだけなので、さらに悪い状況です。確かに、PKは必要ありません。重要な部分はクラスター化インデックスです。ただし、IDENTITYを取得したら、PKを使用することもできます。そして、Uniquifierのウォークスルーに関する私の回答の新しいリンクをご覧くださいplease
Solomon Rutzky

@srutzkyしかし、4バイト整数の一意化子を使用しないので、それを洗浄と見なしています
パラッツォ

1万行未満の場合は問題ありません。たった4バイトの効果に気づく前に、おそらく数百万行にいる必要があります。したがって、「get all rows」クエリの場合、これらのオプションのいずれにも実際の違いはありません。ただし、「get for company_id = @param」クエリの場合、特に、すべての行に対してRIDルックアップを実行する必要がない場合は、company_idによって物理的に順序付けられたデータが役立ちます。
ソロモンRutzky

@srutzkyウォッシュはウォッシュです-10Kまたは1G。これはOPが考慮すべきものにすぎません。
パパラッツォ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.