web-dev-qa-db-ja.com

SQL Serverが非クラスター化インデックスを使用するが、クラスター化インデックスを使用しないのはなぜですか?

1億4500万行のテーブルがあります

CREATE TABLE [dbo].[RFTest](
    [SnapshotKey] [int] NOT NULL,
    [SnapshotDt] [datetime] NOT NULL,
    [LoanNum] [int] NOT NULL,
    [GLSourceSystem] [varchar](10) NOT NULL,
    [FlowDescription] [varchar](30) NULL,
    [Account] [varchar](30) NULL,

    --- plus 20 more column
)

テーブルはSnapshotDtでパーティション分割されます。

テーブルに次のインデックスを追加しました。

create clustered index ci on RFTest (SnapshotDt, SnapshotKey, LoanNum)

create nonclustered index nci on RFTest (SnapshotDt, SnapshotKey, LoanNum)
    include ([GLSourceSystem],[Account],[FlowDescription])

私はクエリの下で実行しました:(テーブル全体を実行したい場合、テーブル全体に長い時間がかかるため、テストには上位100を使用します)

select top 100 *
from RFTest with (index(ci)) -- force index
where LoanNum = 2712

select top 100 *
from RFTest 
where LoanNum = 2712

LoanNum列は両方のインデックスに存在し、クラスター化されたキーの一部であり、非クラスター化に含まれています。

実行計画は、エンジンがクラスター化されたものではなく、クラスター化されていない「nci」インデックスを選択することを示しています。

See Execution plan here

理由を知りたいです。

説明:

どちらの場合も、SQLは同じ量のデータを読み取ります。 LoanNumはインデックスとBTWの両方にあり、LoanNumはキーの一部であるため、クラスタ化インデックスを使用します。

インデックスは私が投稿したとおりです。計画をキャプチャしたとき、クエリにいくつかのコメントがありました。投稿に表示されるクエリは正しいです。両方のインデックスを保持したくないのですが、どちらのパフォーマンスが良いかを確認しようとしたところ、疑問が浮かびました。

3
user71787

オプティマイザには、2つの主要な戦略から選択できます。

  1. すべての行をチェックしてテーブル(クラスター化インデックス)をスキャンし、LoanNum = 2712かどうかを確認します。
  2. スキャンして検索
    • 非クラスター化インデックスをスキャンして、LoanNum = 2712の行を見つけます。
    • 非クラスター化インデックスでカバーされていない、一致した行の列データを検索します。

重要な点は、非クラスター化インデックスのサイズが小さいため、スキャンのコストが低くなることが期待されます。クラスタ化インデックス定義には同じキーがあり、非クラスタ化インデックスには列が含まれているため、これは直観に反するように見えるかもしれませんが、クラスタ化インデックスにはすべての列が含まれているという点が重要ですStored in-row-クラスタ化インデックスのリーフ(最低)レベルは文字通りisin-rowデータです。

予想される一致の数が少ない場合、小さいインデックスをスキャンすることによるコスト削減は、キールックアップを補うのに十分です。

ちなみに、あなたはかもしれませんWHERE 1 = 1クエリから、オプティマイザがクラスタ化インデックススキャンを選択するようにします。 (冗長)定数と定数の比較により、SQL Serverによるクエリのパラメーター化が妨げられるため、推定値はLoanNum 2712に関する統計情報に基づいています。クエリがパラメーター化されている場合、SQL ServerはLoanNum値の平均分布を使用するため、予想される行数が多くなり、プランの選択が変更される可能性があります。

以下も参照してください。

7
Paul White 9

クラスタ化インデックスには、行のすべてのデータが含まれています。実際のプランファイルがないと、インデックスが選択された理由を正確に知ることはできません。私の経験に基づくと、オプティマイザは統計を調べていると言えます。 1行だけを返すため、小さいインデックスを読み取ってデータをシークする方がはるかに高速です。これをテストするには、クラスター化インデックスを強制的に使用します。

大部分の行を返すローン番号の範囲を使用している場合、大量のデータが返されるため、クラスター化インデックスを読み取る方が高速だと思います。

私は自分自身をテストすることができなければ、より良い答えを出すことはできません。これは、2種類のインデックスに関する非常に優れた記事です。

http://www.sqlrelease.com/clustered-vs-nonclustered-indexes-when-to-choose-which-one

0
Joe

スキャンは、ページごとにインデックスをロードすることによって実行されます。 1ページに収まる行数は、インデックスのサイズによって異なります。非クラスター化インデックスを使用したスキャンは、クラスター化インデックスよりも高速になります。これは、ページごとにスキャンできるレコードが増えるためです。

HTH

0