SQL Serverがインデックスを無視するのはなぜですか?


16

CustPassMaster16列のテーブルがありCustNum varchar(8)、その1つがであり、indexを作成しましたIX_dbo_CustPassMaster_CustNumSELECTステートメントを実行すると:

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

インデックスを完全に無視します。これは、CustDataMasterもう少し多くの列(55)を持つテーブルがあり、そのうちの1つが混乱しているためですCustNum varchar(8)IX_dbo_CustDataMaster_CustNumこのテーブルのこの列()にインデックスを作成し、実質的に同じクエリを使用します。

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

そして、作成したインデックスを使用します。

この背後に特定の理由はありますか?なぜインデックスを使用しますが、インデックスを使用しCustDataMasterませんCustPassMasterか?列数が少ないためですか?

最初のクエリは66行を返します。2番目の場合、1行が返されます。

また、追加のメモ:CustPassMaster4991レコードがあり、CustDataMaster5376レコードがあります。これがインデックスを無視する理由になりますか?CustPassMasterまた、同じCustNum値を持つ重複レコードもあります。これは別の要因ですか?

この主張は、両方のクエリの実際の実行計画の結果に基づいています。

CustPassMaster(未使用のインデックスを持つもの)のDDLは次のとおりです。

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

そして、DDL CustDataMaster(多くの無関係なフィールドを省略しました):

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

これらのテーブルのいずれにもクラスター化インデックスはなく、1つの非クラスター化インデックスしかありません。

データ型が保存されているデータの型と完全に一致しないという事実を無視します。これらのフィールドは、IBM AS / 400 DB2データベースからのバックアップであり、互換性のあるデータ型です。(このバックアップデータベースをまったく同じクエリでクエリし、まったく同じ結果を取得できる必要があります。)

このデータはステートメントにのみ使用されSELECTます。バックアップアプリケーションがAS / 400からデータをコピーする場合を除いて、INSERT/ UPDATE/ DELETEステートメントは実行しません。


NonClusteredからClusteredへの転換点に関するこの記事を読む価値があります。sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
マークシンキンソン

3
それが違いです。最初のクエリでインデックスを使用した場合、65回のルックアップを実行する必要があります。これは高価です。2番目のクエリは1つだけ実行する必要があります。
アーロンバートランド

回答:


18

通常、SQL Serverは、基になるテーブルを直接使用するよりもインデックスを使用する方が適切であると判断した場合、インデックスを使用します。

コストベースのオプティマイザは、問題のインデックスを実際に使用する方が費用がかかると考えているようです。実行する代わりにSELECT *、単純にインデックスを使用する場合がありますSELECT T1Col1

SELECT *テーブル内のすべての列を返すようにSQL Serverに指示している場合。これらの列を返すには、SQL Server はテーブル自体(クラスター化インデックスまたはヒープ)からステートメント条件に一致する行のページを読み取る必要がありますWHERE。SQL Serverはおそらく、テーブルから残りの列を取得するために必要な読み取り量は、テーブルを直接スキャンすることを意味すると考えています。実際のクエリと、クエリで使用される実際の実行計画を確認すると便利です。


3
したがって、より明確で最適な解決策は、選択した列を制限し、それらをINCLUDEインデックスの句に含めることですか?
デアKommissar

1
それは非常に大きな違いを生む可能性があります。クエリによって返されたすべての列をINCLUDE句に追加すると、SQL Serverでインデックスが使用される可能性が高くなります。そうは言っても、何を最適化しようとしていますか?テーブルの平均行サイズが100バイトの場合、5000行は約500kbのデータに過ぎず、時間を費やす価値はないかもしれません。
マックスヴァーノン

1
平均行サイズはTable1、で0.30KB、で0.53KBですTable2。このデータはすべてAS / 400(IBM System i)からインポートされ、何にもPKはありません。アプリケーションが時々非常に遅いと人々が言及した後、今日すべてのインデックスを手動で作成しました。
デアコムミサー

10

インデックスを使用する場合、実行しているためselect *、SQL Serverは最初に、where句にある値に一致するインデックスから各行を読み取る必要があります。これに基づいて、各行のクラスター化インデックス値を取得し、クラスター化インデックスとは別にそれらの各値を検索する必要があります(=キールックアップ)。値は一意ではないと言ったので、SQL Serverは統計を使用して、このキールックアップを何回行う必要があるかを推定します。

ほとんどの場合、非クラスター化インデックス+キールックアップのスキャンのコスト推定値はクラスター化インデックススキャンのコスト推定値を超えているため、インデックスは無視されます。

あなたが使用しようとすることができます set statistics io on、インデックスを使用するか、しない場合はI / Oコストが実際に小さいかどうかを確認するために、その後、インデックスヒントを使用します。差が大きい場合、それらが古くなっている場合、統計を調べることができます。

また、SQLが実際に正確な値ではなく変数を使用している場合、パラメータースニッフィング(=プランの作成に使用された以前の値にはテーブルに多くの行がありました)が原因である可能性もあります。


1

それが理由かもしれません。オプティマイザーはコストベースであり、各実行パスが持つ「コスト」に基づいて選択するパスを決定します。「最大の」コストは、ディスクからメモリにデータを取得することです。オプティマイザーが、インデックスとデータの両方の読み取りに時間がかかると計算した場合、インデックスをスキップすることを決定する可能性があります。行が大きくなるほど、より多くのディスクブロックが使用されます。

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