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


169

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

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

前もって感謝します

回答:


187

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

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

パズルのもう1つのピースは、インデックスの使用方法です。3つの典型的なパターンがあります。

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

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

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

優れたクラスター化インデックスは安定しており、エンティティーの存続期間中は変化しません。クラスター化インデックスキーの値を変更すると、行を削除して挿入し直す必要があるためです。

また、ページ分割やフラグメンテーションを回避するために、(FILLFACTORs をいじらずに)良好なクラスター化インデックスはランダムではなく(新しく挿入された各キー値が前の値よりも大きい)順に成長します。

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

例として、販売ファクトテーブルを考えてみましょう。各エントリには、プライマリキーであるIDがあります。ただし、クエリの大半は日付と別の日付の間のデータを要求するため、最適なクラスター化インデックスキーはIDではなく販売です。主キーとは異なるクラスター化インデックスを持つ別の例は、「カテゴリ」や「状態」など、非常に少数の異なる値しか持たないキーなど、非常に低い選択性キーです。左端のキーとしてこの低い選択性キーを持つクラスター化インデックスキーを持つことは、たとえば、特定の「状態」のすべてのエントリを検索する範囲スキャンのために、しばしば意味があります。(state, id)

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


1
「ストップアンドゴーソートを必要とせずにインデックスが順序を満たせる場合、要件による順序付け」とはどういう意味ですか?
マイクシェリル 'キャットリコール'

2
@RemusRusanu。+1非常に便利な答え。例に関する1つの質問(state, id)。この例では、「適切なクラスター化インデックスはランダムではなく順に成長します」という要件を満たしていませんか?では、それを優れたクラスター化インデックスと見なすことができますか?
リジョ

26

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

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

私はPeopleのテーブルがあり、これらの人々にはCountry列と一意の主キーがあるとします。これは人口統計表なので、私が気にするのはこれらだけです。その国にどの国と何人のユニークな人が結びついているか。

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

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

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

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


3
FIYは、SQL Server 6.5を述べた:dba.stackexchange.com/questions/1584/...
GBN

15

主キーがの場合はUNIQUEIDENTIFIER、必ずであることを指定してくださいNONCLUSTERED。クラスター化すると、すべての挿入でレコードを大量にシャッフルして、新しい行を正しい位置に挿入する必要があります。これにより、パフォーマンスが向上します。


1
クラスター化されたキーのUUIDを回避しようとしていますが、上記の推論は不完全であると考えています。SQLサーバーは、正しい位置にaを挿入するために行を必ずしもシャッフルしません(「より低い値とより高い値の間」を意味する場合)。1兆行のテーブルの中央に挿入することを検討してください。余分な間接化が必要です。これはあなたが意図したものかもしれません。シーケンシャルUNIQUEIDENTIFIERタイプも存在し、一意のキーを生成する確率は同じですが、128サイズという問題があります。
チャールズバーンズ

7

非常に一般的な例:

  • CustomerCustomerIDasを持つテーブルCLUSTERED PRIMARY KEY
  • 注文表とOrderID (PK), CustomerID, OrderDateその他の列
  • OrderPositionsOrderPositionID (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それはその後、いくつかの(挿入順序に依存する)ランダムなものを実際の(使用可能な)データを追加するためにはるかに理にかなって- uniquinessを確保するために。

顧客は(願わくば)複数の注文を行うため、OrderID(通常、このために並べ替える場合)または(OrderDate日時の場合-それ以外の場合、顧客は1日に1注文に制限されます)のいずれかを追加する必要がありますそしてCLUSTERED INDEX、で終わる:

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

同じ規則がOrderPositionsテーブルに適用されます。通常、ほとんどのクエリは特定の順序ですべての位置をリストするため、OrderPositionIDas NONCLUSTEREDおよびUNIQUE CLUSTERED INDEXon を使用してPKを作成する必要がありますOrderId, OrderPositionID

ところで:CustomerテーブルがそのPKによってクラスター化されていることは正しいです(CustomerID「トップレベルテーブル」であるため、通常のアプリケーションでは、ほとんどがそのCustomerIDによってクエリされます)。

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


2

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

パフォーマンスの尺度の例は、単一クエリ時間(速度)、テーブルに対する合計クエリ時間の統合(効率)、およびクラスター化(サイズと同様のパフォーマンスを達成するために非常に大きな非クラスター化インデックスに多くのinclude列を追加する必要がある) )。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.