4

1900年1月1日から2100年12月31日までの日付のリストを含む単純な日付テーブル(Date、DateID)があります。

演算子とハードコードされたパラメーター値を使用してテーブルから選択するbetweenと、2つの実際の行と比較して3つの推定行を持つ正しいクエリプランが得られます。

select v.Date from Dates v
where v.Date between '20130128' and '20130129';

ただし、ハードコードされた値をパラメーターに置き換えると、クエリプランは非常に貧弱なプランに変わり、推定行数は6000を超え、実際の行数は2つだけになります。

select v.Date from Dates v
where v.Date between @startdate and @enddate;

クエリプラン自体は同じです。パラメータ化されたクエリの実行速度がハードコードされたクエリの約4倍になるのは、推定行の違いだけです。パラメータ化されたバージョンの実行が非常に遅くなる理由、および正しいクエリプランを使用するためにSQL Serverに提供できるインデックス/ヒントについて、私が見逃していることはありますか?

いくつかの追加情報:

  • 単純な等式基準を使用する場合、問題は発生しません。これは、オペレーター=に固有のようです。between
  • パラメータ化されたクエリの最後に追加option(recompile)すると、ハードコードされたクエリと同じ完全なクエリプランが得られます。
  • 日付テーブルには、Date列とDateID列の2つの列のみがあり、主キーのDateID列にクラスター化インデックスがあり、Date列に一意の非クラスター化インデックスがあります。すべてが統計を更新しました。
  • クエリプランは、ハードコードされたクエリの自動パラメータ化を実行し、ハードコードされた値を@1と@2に置き換え、クエリを大文字で表示します。パラメータ化されたクエリに対して変換を実行していないようです。
  • SQL Server2008R2の使用。

私は、これが何らかのパラメータスニッフィングの問題であると疑うことを理解するのに十分知っています。 クエリに追加してみませんか?これは、はるかに大規模で複雑なクエリの一部として使用されています。SQLServerに処理を任せ、可能な場合はキャッシュからクエリプランを再利用することをお勧めします。option(recompile)

編集と更新:これまでの思慮深い回答に感謝します。質問をさらに絞り込むために、クエリプランは上記の両方のクエリに完全に適切なインデックスを使用しますが、パラメータ化されたクエリの日付範囲が2日しかないことを認識しないのはなぜですか、代わりに範囲が6000であると見なすのはなぜですか行幅?特に、クエリプランを見ると、SQLServerがハードコードされたクエリの自動パラメーター化を実行している場合はどうでしょうか。基になるクエリプランでは、両方のプランがパラメータ化されているという点で同じように見えます。

4

2 に答える 2

3

クエリ プランは、クエリを最初に実行したときのパラメーター値に基づいています。これは、パラメーター スニッフィングと呼ばれます。を追加するoption (recompile)と、実行ごとに新しい計画が生成されます。

クエリ プランは、SQL クエリのハッシュに基づいてキャッシュされます。したがって、クエリの両方のバージョンに対して異なるキャッシュ スロットがあります。

追加することoption (recompile)は良い解決策です。以下を使用することもできます。

option (optimize for (@startdate = '20130128', @enddate = '20130129'));

それらの値が渡されたかのようにクエリ プランを生成するため。

テストのために、次のコマンドを使用してキャッシュからすべてのプランを削除できます。

DBCC FREEPROCCACHE
于 2013-02-13T10:41:32.250 に答える
2

この問題は、パラメータスニッフィングが原因である可能性があります。これは、最初に渡したパラメータに基づいてクエリプランを最適化するために設計された手法です。

過去に日付パラメーターを使用したパラメータースニッフィングで悪い経験をしたことがあります-パラメータースニッフィングでは、クエリに最初に入力された値がクエリの一般的な使用法を表していない可能性があります(結果として、 -通常の値)ただし、特に日付パラメータを使用すると、結果のクエリプランは、提供されたすべての値に対して実際には非常に非効率的であることがよくあります。なぜこれがパラメータスニッフィングを無効にしているのかわかりませんが、一般的には修正されます。

を使用してSQLServerのパラメータースニッフィングを防ぐことができますOPTIMIZE FOR UNKNOWN(これを使用したことがないため、動作を保証できません)。あるいは、パラメーターをローカル変数にコピーすると、SQLServerがパラメータースニッフィングを実行できなくなるようです。

-- Perform the query using @StartDateLocal and @EndDateLocal
DECLARE @StartDateLocal DATETIME;
SET @StartDateLocal = @StartDate;

この方法でパラメータスニッフィングを無効にすると、クエリプランのコンパイルは通常、クエリがかなり遅い場合を除いてクエリの実行コストに比べて比較的コストがかかるため、クエリプランの再コンパイルを毎回強制するよりも優れています。

于 2013-02-13T10:53:01.447 に答える