OUTER APPLYステートメントでネストされたクエリから選択する場合、ネストされたクエリは特定の状況で1回だけ評価されるようです。
Azureフィードバックフォーラムに報告されたバグ: https://feedback.Azure.com/forums/908035-sql-server/suggestions/39428632-Microsoft-sql-server-2014-incorrect-result-when-s =
これは予期された動作ですか、それともドキュメントに何か不足していますか、それともSQL Serverのバグですか?
また、すべての行に対してネストされたクエリの評価を強制する可能性はありますか?
テストケース1
VALUESのすべての行のネストされたFROMクエリを評価します(予想外の動作)
SELECT
v,
v2
FROM
(VALUES (1), (2), (3), (4)) AS inner_query(v)
OUTER APPLY (
SELECT
MAX(inner_v2) AS v2
FROM (
SELECT
15 AS id,
v AS inner_v2
) AS outer_query
GROUP BY id
) AS outer_apply
結果:
| v | v2|
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
テストケース2
また、VALUESのすべての行に対してネストされたFROMクエリを評価します(予想外の動作)
SELECT
v,
v2
FROM
(VALUES (1), (2), (3), (4)) AS inner_query(v)
OUTER APPLY (
SELECT
MAX(inner_v2) AS v2
FROM (
SELECT
15 AS id,
v AS inner_v2
UNION ALL
SELECT
id AS id,
TestCaseTemp2.v AS inner_v2
FROM
(VALUES (1337, 0)) AS TestCaseTemp2(id, v)
WHERE TestCaseTemp2.v != 0
) AS outer_query
GROUP BY id
) AS outer_apply;
結果:
| v | v2|
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
テストケース3
ネストされたFROMクエリを1回だけ評価します
CREATE TABLE TestCaseTemp
(
id int,
v int
);
INSERT INTO TestCaseTemp VALUES (1337, 0);
SELECT
v,
v2
FROM
(VALUES (1), (2), (3), (4)) AS inner_query(v)
OUTER APPLY (
SELECT
MAX(inner_v2) AS v2
FROM (
SELECT
15 AS id,
v AS inner_v2
UNION ALL
SELECT
id AS id,
TestCaseTemp.v AS inner_v2
FROM
TestCaseTemp
WHERE TestCaseTemp.v != 0
) AS outer_query
GROUP BY id
) AS outer_apply;
DROP TABLE TestCaseTemp;
結果:
| v | v2|
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
これはSQL Serverのバグですか?
はい、確かに、最終結果のすべての行に返される_1
_は、外部入力の最初の行にのみ存在するため、後続の行のスコープにも含めないでください。 Paul White here が詳細に検討したのと同じ基本的な問題のように見えます。
私は最後のクエリをdbfiddle(SQL Server 2019)で実行し、ここに計画を貼り付けました https://www.brentozar.com/pastetheplan/?id=Sy4sBB5lI ソートが4回実行されるようです(1回だけ)外側の行ごとに)が、何らかの理由で再バインドではなく巻き戻しなので、その種類の子演算子を2回以上呼び出さないでください。これはバグであり、外部結合(_Union1004
_)の相関パラメーターへの参照により、値が変更されたときに再バインドが発生します。その結果、プランのNode 5での_Union1004
_への参照は再評価されません。
すべての行に対してネストされたクエリの評価を強制する可能性はありますか?
クエリヒントの追加OPTION (MERGE UNION)
はあなたの例で機能しますが、これがすべてのケースでバグを回避するのに必ずしも十分であるかどうかはわかりませんが、リンクされたPaul Whiteの回答からは機能するはずです。あなたの例の場合、それはソートで 下にプッシュダウン として機能するため、結合された結果全体ではなく、TestCaseTemp
行のみを巻き戻します。適切なインデックスを追加して、並べ替えを完全に削除することもできます。