ウィンドウ関数は、外部パラメーター化された 'where'句を含むビューから呼び出されると、ひどい実行プランを引き起こします


10

私はずっと前にこの問題を抱えていましたが、自分に合った回避策を見つけ、それを忘れていました。

しかし、今はその問題が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は主キーですか?列とパラメーターのデータ型は一致しますか?
gbn

order_numberは主キーではありません。それはint not null両方のテーブルで、その上に非クラスタ化インデックスを持ちます。
GSerg

5
SQL Server 2005には、この領域での述語のプッシュに関する問題がありました。私はそれらが今修正されたと思いました。ところで、TSQLの例では、パラメータではなく変数を使用しています。追加はOPTION (RECOMPILE)役立ちますか?
マーティン・スミス

1
@GSerg-では、フィルターが最後の悪い計画では、フィルターに500万行と推定され、実際と一致する10行が推定されますか?もしそうなら、それはその述語プッシュの問題がまだ完全に修正されていないことです。
マーティン・スミス

回答:


5

これは、何らかの形で再浮上を続け、SQL Server 2012にもまだ存在している、長期にわたる問題のようです。

それを議論するいくつかの投稿は

2012年までのSQL Serverの現在のすべてのバージョンoption(recompile)は、使用されている場合(2008以降の場合)を除いて、パラメーター化された述語のシーケンスプロジェクトを過ぎてパーティショングループにフィルターをプッシュできません。

recompileヒントの代わりに、@ a1ex07で提案されているように、パラメーター化されたインラインTVFを使用するようにクエリを書き直すこともできます)


SQL Server 2014でも同様
Guillaume86

3

ビューをテーブル値のudfに置き換えてみます。このようにして、最初にレコードをフィルターし、次にウィンドウ関数を適用します。この関数はテーブルパラメータを受け入れるので、複数order_numberを渡すことができます


さらに別の回避策、はい。すべてのクライアントがテーブル値関数を使用できるわけではなかったので、私はそれを行うことができませんでした。
-GSerg

どうして?私は100%わからないんだけど、私はあなたが必要とするすべてのようなものにはほとんどのクエリ変更することだと思うSELECT * FROM my_funct(12345)
a1ex07

要件の1つは、Excelを使用するエンドユーザーが(つまり、MSクエリによって)クエリを使用できることであり、MSクエリでは、少なくとも2003
。– GSerg

it will filter records first, and then apply window function間違っている。実行に決定的な順序はありません
Remus Rusanu 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.