私はずっと前にこの問題を抱えていましたが、自分に合った回避策を見つけ、それを忘れていました。
しかし、今はその問題がSOにあるので、この問題を取り上げるつもりです。
非常に簡単な方法(注文+注文明細)でいくつかのテーブルを結合するビューがあります。
where
句なしでクエリを実行すると、ビューは数百万行を返します。
しかし、誰もそれをそのように呼ぶことはありません。通常のクエリは
select * from that_nasty_view where order_number = 123456;
これは、5分のうち約10レコードを返します。
重要なこと:ビューにはウィンドウ関数が含まれています。ウィンドウ関数はrank()
、ビューが常に照会されるフィールドによって正確に分割されます。
rank() over (partition by order_number order by detail_line_number)
さて、このビューがクエリ文字列のリテラルパラメータでクエリされた場合、上記とまったく同じように、ビューは即座に行を返します。実行計画は問題ありません:
- インデックスを使用して両方のテーブルのインデックスシーク
order_number
(10行を返します)。 - 返された小さな結果に対するウィンドウの計算。
- 選択。
ただし、ビューがパラメーター化された方法で呼び出されると、状況が悪化します。
Index scan
インデックスを無視してすべてのテーブルで。5m行を返します。- 巨大な参加。
- すべて
partition
のsのウィンドウを計算しています(約500kウィンドウ)。 Filter
5mから10列を取る。- 選択する
これは、パラメーターが関係するすべての場合に発生します。それはSSMSである場合もあります:
declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;
これは、ExcelなどのODBCクライアントにすることができます。
select * from that_nasty_view where order_number = ?
または、SQL連結ではなくパラメーターを使用する他のクライアントでもかまいません。
ウィンドウ関数がビューから削除された場合、パラメーターで照会されたかどうかに関係なく、完全に迅速に実行されます。
私の回避策は、問題のある関数を削除し、後でそれを再適用することでした。
しかし、何ができますか?SQL Server 2008がウィンドウ関数を処理する方法に本当にバグがあるのですか?
order_number
は主キーではありません。それはint not null
両方のテーブルで、その上に非クラスタ化インデックスを持ちます。