web-dev-qa-db-ja.com

主キーを非クラスター化として宣言する必要があるのはいつですか?

以前に尋ねた別の質問のテストデータベースを作成しているときに、主キーをNONCLUSTEREDと宣言できることを思い出しました

NONCLUSTERED主キーではなく、CLUSTERED主キーをいつ使用しますか?

前もって感謝します

177
Stuart Blackler

問題は、「いつPKをNCにするべきか」ではなく、「クラスター化インデックスの適切なキーは何か」と尋ねるべきです。

そして答えは本当にどのようにデータをクエリするかに依存します。クラスタ化インデックスは、他のすべてのインデックスよりも優れています。常にすべての列が含まれるため、常にカバーされます。したがって、クラスター化インデックスを活用できるクエリでは、予測された列や述語の一部を満たすためにルックアップを使用する必要はありません。

パズルのもう1つのピースはどのようにインデックスを使用できるかです? 3つの典型的なパターンがあります。

  • プローブ、インデックスで単一のキー値がシークされるとき
  • キー値の範囲が取得されたときの範囲スキャン
  • 要件による順序付け、インデックスがストップアンドゴーソートを必要としない順序付けを満たすことができる場合

したがって、予想される負荷(クエリ)を分析し、多数のクエリが特定のインデックスを使用することを発見した場合、それらは特定のインデックスのアクセスパターンを使用するため、インデックスの恩恵を受けるため、そのインデックスをクラスター化インデックスとして提案することは理にかなっています。

さらに別の要因は、クラスター化インデックスキーがall非クラスター化インデックスで使用されるルックアップキーであるため、広いクラスター化インデックスキーが波及効果を生み出し、すべての非クラスター化インデックスを広げます。ワイドインデックスは、ページ数、I/O数、メモリ量が多くなることを意味します。

適切なクラスター化インデックスはstableです。クラスター化インデックスキーの値の変更は、行が削除して挿入し直してください。

また、適切なクラスター化インデックスは、ページ分割や断片化を回避するために(FILLFACTORsをいじることなく)、ランダムではなく(新しく挿入された各キー値が前の値よりも大きく)成長します。

さて、良いクラスター化インデックスキーがわかったところで、主キー(データモデリングの論理プロパティ)は要件に一致していますか?はいの場合、PKをクラスター化する必要があります。いいえの場合、PKはクラスター化されていません。

例として、売上ファクトテーブルを考えます。各エントリには、主キーであるIDがあります。ただし、クエリの大部分は日付と別の日付の間のデータを要求するため、最適なクラスター化インデックスキーはdateであり、[〜#〜] id [〜#〜]。主キーとは異なるクラスター化インデックスを持つ別の例は、「カテゴリ」または「状態」のような非常に選択性の低いキーであり、明確な値がほとんどないキーです。左端のキーとして、この選択性の低いキーを持つクラスター化インデックスキーを持ちます。 (state, id)、特定の「状態」にあるすべてのエントリを探す範囲スキャンのため、多くの場合意味があります。

heapを超える非クラスター化主キーの可能性に関する最後の注意(つまり、クラスター化インデックスがまったくない)。これは有効なシナリオである可能性があります。一般的な理由は、クラスター化インデックスと比較してヒープの一括挿入スループットが大幅に向上するため、一括挿入のパフォーマンスが重要な場合です。

194
Remus Rusanu

クラスタ化インデックスを使用する基本的な理由は Wikipedia に記載されています:

クラスタリングは、データブロックを特定の異なる順序に変更してインデックスと一致させ、行データが順番に格納されるようにします。したがって、特定のデータベーステーブルで作成できるクラスター化インデックスは1つだけです。クラスター化インデックスcan全体的な検索速度が大幅に向上しますが、通常はクラスター化インデックスと同じまたは逆の順序でデータに順次アクセスする場合、またはアイテムの範囲が選択されています。

私がPeopleのテーブルを持っているとします。これらの人々はCountry列と一意の主キーを持っています。これは人口統計の表なので、私が気にするのはこれらだけです。どの国に何人のユニークな人々がその国に関連付けられています。

したがって、国の列をSELECT WHEREまたはORDER BYするだけです。主キーのクラスター化インデックスは役に立ちません。PKでこのデータにアクセスするのではなく、この他の列でアクセスします。テーブルにはクラスター化インデックスを1つしか持てないため、PKをクラスター化として宣言すると、国でクラスター化インデックスを使用できなくなります。

さらに、これは クラスター化インデックスと非クラスター化インデックス に関する優れた記事です。クラスター化インデックスがSQL Server 6.5で挿入のパフォーマンスの問題を引き起こしたことがわかります(少なくとも、ここにいるほとんどの人には関係ないはずです)。

IDENTITY列にクラスター化インデックスを配置すると、すべての挿入がテーブルの最後のページで行われ、そのページは各IDENTITYの期間中ロックされます。大したことはありません...全員が最後のページを必要とする5000人がいない限り。次に、そのページには多くの競合があります

これは、それ以降のバージョンでは当てはまらないことに注意してください。

27
Ben Brocka

主キーがUNIQUEIDENTIFIERである場合は、NONCLUSTEREDであることを指定してください。それをクラスター化すると、すべての挿入で新しい行を正しい位置に挿入するために、一連のレコードをシャッフルする必要があります。これは戦車性能になります。

17
Bryan Johns

非常に一般的な例:

  • Customerを_CLUSTERED PRIMARY KEY_として含むCustomerIDテーブル
  • OrderID (PK), CustomerID, OrderDateと他のいくつかの列を含む注文テーブル
  • OrderPositions with OrderPositionID (PK), OrderId, ProductID, Amount, Price ...
  • orderテーブルにインデックスを付ける必要があります

もちろん、「依存する」は-ほとんどの場合-正解ですが、ほとんどのアプリケーション(BIレポートではない)は顧客ベースで機能します(たとえば、顧客278としてWebサイトにログインし、[注文]をクリックするか、店員が顧客4569のすべての注文をリストするか、請求ルーチンが顧客137のすべての注文を合計します。

この場合、OrderIDでテーブルをクラスター化してもあまり意味がありません。はい、クエリを_SELECT ... WHERE OrderId = ?_として注文の詳細を一覧表示することになりますが、これは通常短くて安価な(3読み取り)インデックスシークです。

一方、OrderテーブルをCustomerIDでクラスター化すると、テーブルで_CustomerId = ?_をクエリするたびに複数のキー検索を行う必要がなくなります。

_CLUSTERED INDEX_は常にUNIQUEである必要があります。そうでない場合、SQL Serverは不可視(=使用不可)INT列UNIQUIFIERを追加して一意性を確保します。実際の(使用可能な)データをランダムに追加してから(ランダムに依存)挿入順序)もの。

顧客は(うまくいけば)複数の注文を行うので、OrderIDまたは(通常これをソートする場合)OrderDate(日時の場合)を追加する必要があります-そうでない場合、顧客は1日あたり1つの注文に制限されます)を_CLUSTERED INDEX_に追加すると、次のようになります。

CREATE UNIQUE CLUSTERED INDEX IX_Orders_UQ on Orders (CustomerID, OrderID)

同じ規則がOrderPositionsテーブルに適用されます。通常、ほとんどのクエリは特定の順序ですべてのポジションをリストするので、OrderPositionIDNONCLUSTEREDとして、_UNIQUE CLUSTERED INDEX_を_OrderId, OrderPositionID_としてPKを作成する必要があります。

ところで、CustomerテーブルはそのPK(CustomerID、 "トップレベルテーブル"であり、通常のアプリケーションでは主にCustomerIDによって照会されるため)によってクラスター化されているのは正しいことです。

純粋なルックアップテーブル。 GendersまたはInvoiceTypesまたはPaymentTypeは、PKによってクラスター化する必要があるテーブルの別の例です(通常、GenderIdInvoiceTypeIdまたはPaymentTypeIdで結合するため)。

8
Thomas Franz

パフォーマンスの指標を使用して、クラスター化インデックスがクラスター化PKよりもシステム全体にとって有益であると見なされる場合。テーブルに存在できるクラスター化インデックスは1つだけです。

パフォーマンスの測定基準の例としては、単一クエリ時間(速度)、テーブルに対する合計クエリ時間の統合(効率)、クラスター化(サイズ)と同様のパフォーマンスを実現するために、非常に大きな非クラスター化インデックスに多数のインクルード列を追加する必要があります。 )。

これは、一意ではない、NULLを含む(PKでは許可されない)インデックスを使用してデータが通常取得される場合、またはPKが副次的な理由(レプリケーションや監査証跡レコードの識別など)で追加された場合に発生します。

2
crokusek