2009-08-31 16 views
7

私はSQL Server 2005でストアドプロシージャを持っています。実行したときに実行計画を見ると、クラスタ化インデックススキャンが実行されていることがわかりました。これには84%のコストがかかります。私はクラスタード・インデックスを得るためにいくつかのことを変更しなければならないことを読んだが、変更すべきものはわからない。Clustered Index Scanの代わりにClustered Index Seekを取得するにはどうすればよいですか?

私はこれに助けていただければ幸いです。クエリは、テーブル内の行の一定割合以上をinclduesた場合、それがすると予測しているため、オプティマイザは、代わりに求めるのスキャンを実行することを選択します

おかげで、

ブライアン

+1

クエリとテーブルのスキーマはどうですか? –

答えて

19

問題が何であるかを推測するのは難しいですし、問題が全くないかどうかもわかりません。シークの代わりにスキャンを選択することは、多くの要因によって駆動される可能性があります。

  • クエリは、テーブル全体をカバーする結果セットを表します。つまりクエリは単純なSELECT * FROM <table>です。これは、他の何かを考慮する必要がないクラスタ化インデックススキャンによって完全にカバーされる簡単なケースです。
  • オプティマイザには選択肢がありません:
    • クエリは、テーブル全体のサブセットを表現しますが、フィルタリング述語は、クラスタ化されたキーの一部ではない列にあり、それらの列には非clustredインデックスがありませんどちらか。これらはフルスキャン以外の代替計画ではありません。
    • クエリには、クラスタ化インデックスキーの列に対するフィルタリング述語がありますが、SARGableではありません。フィルタリング述語は、通常、SARGableにするために書き直す必要があります。適切な書き換えは、場合によって異なります。暗黙的な変換ルールのために、より微妙な問題が発生する可能性があります。フィルタリング述語はWHERE column = @valueですが、列はVARCHAR(Ascii)で、@valueはNVARCHAR(Unicode)です。
    • クエリでは、クラスタ化されたキーの列にSARGaleフィルタ述語がありますが、一番左の列はフィルタされません。つまりクラスタ化インデックスは列(foo, bar)にありますが、WHERE句はbarのみにあります。
  • オプティマイザは、スキャンを選択します。
    • 代替は、非クラスタ化インデックスである場合には、その後のスキャン(または範囲求める)が、選択は、クラスタ化インデックスに原因を使用することですすることができ、通常はは、非クラスタ化の欠如にindex tipping pointまで追跡クエリ投影のインデックスカバレッジこれはあなたの質問ではないことに注意してください。クラスタ化されたインデックスのシークではなく、クラスタ化されていないインデックスのシークを期待しています(100%正確で文書化されています...)
    • カーディナリティの見積もり。クエリのコスト見積もりは、結果の基数(つまり、一致する行の数)の見積もりを提供するクラスタ化インデックスキー統計に基づいています。シンプルなクエリの場合シークまたはレンジシークの見積もりは、統計情報がどのようになっても、スキャンの場合よりも低くなる可能性がありますが、複合クエリの場合、複数のテーブルの結合およびフィルタを使用します事象はより複雑であり、プランはかもしれませんは、照会オプティマイザがオブザーバが期待するものとジョイン評価の順序が逆になるプランを選択する可能性があるため、シークが予想されるスキャンを含みます。逆順の選択は正しい(ほとんどの時間)か問題があるかもしれません(通常は統計が古くなっているか、パラメータスニッフィングのためです)。
    • 注文保証。スキャンは保証された順序で結果を生成し、実行ツリー上の要素はこの順序の恩恵を受ける可能性があります(ソートまたはスプールを削除するか、ハッシュ/ネストされた結合の代わりにマージ結合を使用するなど)。全体的に、アクセス・パスが明らかに遅いと判断された結果、照会コストが向上します。

これら は、クラスタ化インデックスのシークが予想される場合、クラスタ化インデックススキャンが存在することができる理由をいくつかの簡単なポインタです。質問は極端に一般的なものであり、8ボールに頼る以外の理由で「なぜ」という答えを出すことは不可能です。あなたが正しく文書化され、正しく連結されていることを確認したら、クラスター化インデックスを期待してをシークすると、クラスター化されたキー値に基づいてユニークなレコードを検索しています。この場合、問題はWHERE句のSARGabilityでなければなりません。

+0

"シンプルなクエリでは、シークまたはレンジシークの見積もりがスキャンの見積もりよりも低くなるため、これは起こりません" ...本当ではない...行の10%だけが 'Special = true(1 ) 'の場合、単純なクエリ' Select * From table where Special = 0'は常にテーブルスキャンを選択します。これは、索引がクラスタード索引でない限り、特殊列に索引が存在する場合でも同じです。 (インデックスがクラスタード・インデックスの場合、オプティマイザはもちろんレンジ・シークを選択します)。 –

+0

@Charles:これは私が先ほど触れた「インデックス・ティッピング・ポイント」です。 Btw、あなたのサンプルクエリは 'シンプル'ではありません(例:Trivial、http://msdn.microsoft.com/en-us/library/aa226174%28SQL.70%29.aspxを参照)。列はユニークなカバー索引にあり、その列のセットを持つ他の索引はありません。 –

+0

あなたが「シンプルな」という意味であれば、私は技術的に同意することができますが、「シンプルな」という単語の意味を明確にしていれば、読者は非常に違った印象を受けるでしょう。シンプルな言葉を引用符で囲んだりハイライトしたり、yrの定義を括弧に入れたり、脚注として書いたりすると、読者は「シンプルな」クエリにはこの問題はなく、正しくないと仮定します。あなたが言及する理由。 ...問題を悪化させるためには、ティップの問題とは関係のない、複雑に結合するために複雑なものを半定義します。 –

5

(シークの場合は、各行の索引に1つのディスクIOが必要です)、スキャンの場合は、表全体で1行に1つのディスクIOしかありません。

b-treeインデックスに5つのレベルがある場合、クエリがテーブルの行の20%以上を生成する場合は、テーブル全体を読み取るのが各IOに対して5つのIOよりも安い20%の行のうち...

プロセスのこのステップで返される行の数を減らすために、クエリの出力をさらに絞り込むことはできますか?それはそれがスキャン上のシークを選択するのに役立ちます。

関連する問題