web-dev-qa-db-ja.com

変数が正しく推定されていないクエリ

いくつかの結合でうまく機能していないクエリがあります。多くのトリアージの後、クエリを分解して、運転テーブルだけを見ることにしました。以下は、駆動テーブルのみに基づくクエリの例です。

_DECLARE @VAR AS INT

SET @VAR = 11652862

SELECT Field1, Field2
FROM tablea
WHERE Field2 = @VAR
_

コードをそのまま実行すると、実際の実行プランは15kの推定行と1mの実際の行で返されます。はい、これは単純な_SELECT...FROM TABLE_ですが、これはより大きなクエリの一部にすぎないことを忘れないでください。したがって、結合を追加すると、この見積もりの​​違いは大幅に悪化します。

オプティマイザーはクエリをプランにコンパイルするときに変数の値を知らないため、問題は_@VAR_であることがわかっています。そのため、フィールドの見積もりの​​平均行数が返されます。 OPTION (RECOMPILE)を追加すると、コンパイル時に値が強制され、正しい行数で返されます。

キッカーは次のとおりです。これは、私の会社が編集できるカスタムコードではありません。編集できないサードパーティのアプリの一部です。統計を更新しても役に立ちません(ヒストグラムを調べたところ、変数の値が正しくリストされています)。私はすでにこれらの2つのフィールドにカバーするNCインデックスを持っています。私が考えることができる唯一のオプションは、このクエリで強制実行プランを使用することです。しかし、私はそれをうまく行うことができたことがありません。強制実行計画に関する他のアイデアや優れたハウツーリファレンスを持っている人はいますか?

コメントで尋ねられたので、インデックスのcreateステートメントは次のとおりです。

_CREATE NONCLUSTERED INDEX idx ON tablea (Field2)
_
2
Chris Woods

プランガイド以外のアイデアもありますので、ここに掲載しますが、実際に実装することはお勧めできません。 tableaから行の見積もりを増やすには、少なくとも2つの異なる方法があります。 1つ目は統計を手動で設定することで、もう1つは、テーブルに直接アクセスする代わりに、定義したビューを使用するようにサードパーティアプリをリダイレクトすることです。

CREATESTATISTICSとUPDATESTATISTICSの両方にSTATS_STREAMオプションがあります。 BooksOnlineがそれについて言っていることは次のとおりです。

情報提供のみを目的として識別されます。サポートされていません。将来の互換性は保証されません。

それがあなたにそれを使用することを止めさせるかどうかは完全に理解できます、しかしそれがあなたにできることはあるオブジェクトから別のオブジェクトに統計を移植することです。 tableaを使用するクエリからのカーディナリティの推定値を50倍に増やしたいとします。 tableaからデータを取得し、それを50回複製し、FULLSCANで統計を収集してから、他のテーブルのSTATS_STREAM値とNORECOMPUTEオプションを使用して、tableaの統計を更新できます。おそらく、ROWCOUNT値も変更する必要があります。これは、テーブルを参照するすべてのクエリにかなり大きな影響を与えるはずです。他のクエリで問題が発生したり、テーブル内の基になるデータが変更されたり、インデックスの統計が更新されなかったりする可能性があります。これは良いオプションではありません。

サードパーティアプリケーションがSQLServerに接続する方法によっては、tableaとは異なるスキーマでビューを作成し、アプリケーションのその部分をビューで使用するように指示できる場合があります。ビューには、tableaと同じ列がすべて含まれている必要がありますが、返される行数を増やす追加のコードが含まれている必要があります。非常に実用的ではないように思われるため、これを機能させるために一生懸命努力しませんでしたが、このアプローチは、 並列計画の強制 に関するAdamMachanicの記事に触発されました:

SELECT ta.*
FROM tablea ta
CROSS JOIN (
    SELECT t.t
    FROM
    ( 
    VALUES 
    (2), (1), (1), (1), (1), (1), (1), (1), (1), (1), 
    (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), 
    -- lots more values here ...
    (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)
   ) t(t)
WHERE t % 2 = 0
) t
OPTION (QUERYTRACEON 8690);

私はそのクエリに本当に満足していませんでしたが、あまり多くの時間を費やしませんでした。その形式では、テーブルの1回のスキャンを実行しますが、クエリオプティマイザはカーディナリティの推定値を100倍過大評価します。 TF 8690は、無意味なテーブルスプールを防ぐためにありますが、これはビューやより大きなクエリではおそらく適切ではありません。同じアプリケーションがこのテーブルにデータを挿入する場合は、何らかの方法でそれを処理する必要があります。これは良いオプションではありません。

繰り返しになりますが、これらのオプションのいずれかを試すべきではないと思います。ベンダーに相談するか、それがうまくいかない場合は、それを吸い上げて計画ガイドで再試行する方がはるかに良いでしょう。私はそれらの経験がないので、私は助けることができません。

1
Joe Obbish