今日はあまりにも多くの会議がありましたが、私はまだ私の頭脳を持っていると思います。クエリのパフォーマンスを向上させるために、次の謎に遭遇しました(テーブル名とフィールドの言い換え)。
SELECT X.ADId FROM
(
SELECT DISTINCT A.ADId
FROM P WITH (NOLOCK)
INNER JOIN A WITH (NOLOCK) ON (P.ID = A.PId)
INNER JOIN dbo.fn_A(16) AS VD ON (VD.DId = A.ADId)
LEFT JOIN DPR ON (LDID = A.ADId)
WHERE ((A.ADId = 1) OR ((HDId IS NOT NULL) AND (HDId = 1))) AND
(P.PS NOT IN(5,7)) AND (A.ASP IN (2, 3))
) X
WHERE (dbo.fn_B(X.ADId, 16) = 1)
ご覧のとおり、内部クエリの内容はほとんど関係ありません。最初の要点は、ADIdの値が重複しているため、すべてのレコードでfn_B()が呼び出されないようにしたかったので、内部でSELECT DISTINCTを実行してから、個別のレコードをフィルタリングしました。合理的に聞こえますか?
ここから謎が始まります...
内部クエリは、(指定されたパラメータに対して)NORECORDSを返します。「WHEREfn_B()= 1」をコメントアウトすると、クエリはゼロ時間で実行されます(結果は返されません)。元に戻すと、クエリには6〜10秒かかり、結果は返されません。
これは常識、または少なくとも私の常識を打ち負かすようです:-)内部クエリがデータを返さない場合、外部条件は決して正しく評価されるべきではありませんか?
もちろん、時間をかけて実際の実行計画を確認し、保存して、慎重に比較しました。それらは99%同一であり、気付くのは珍しいことではありません。
私はいくつかのCTEをだまして、最初のCTEでクエリ結果を取得し、それをレコードをフィルタリングしないことが保証されているいくつかの条件を持つ2番目のCTEに渡し、すべてのCTEの外部でfn_B()呼び出しを評価しましたが、動作は正確でした同じ。
また、古いクエリ(同じ値でfn_B()を複数回呼び出す可能性がある)を使用するなど、他のバリエーションでも同じ動作をしました。条件を削除すると、ゼロ時間でレコードが取得されません。元に戻すと、10秒以内にレコードがありません。
誰かアイデアはありますか?
御時間ありがとうございます :-)
PS1:簡単なクエリを使用してtempdbの状況を再現しようとしましたが、実現できませんでした。それは私の実際のテーブルでのみ発生します。PS2:このクエリは別の関数内で呼び出されるため、結果を一時テーブルに入れてさらにフィルタリングすることも問題外です。