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がハードコードされたクエリの自動パラメーター化を実行している場合はどうでしょうか。基になるクエリプランでは、両方のプランがパラメータ化されているという点で同じように見えます。