从 OUTER APPLY 语句中的嵌套查询中进行选择时,嵌套查询似乎在某些情况下只被评估一次。
向 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 查询
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 |
是的,当然,
1
最终结果中所有行中返回的 仅存在于外部输入的第一行中,因此甚至不应该在后续行的范围内。它看起来与 Paul White在这里详细讨论的基本问题相同。我在 dbfiddle (SQL Server 2019) 中执行了您的最终查询并在此处粘贴了计划https://www.brentozar.com/pastetheplan/?id=Sy4sBB5lI 看起来排序执行了 4 次(每个外行一次)但是对于出于某种原因,它会倒带而不是重新绑定,因此不会多次调用该排序的子运算符。这是一个错误,因为对外部连接 ( ) 的相关参数的引用
Union1004
应该会在其值更改时导致重新绑定。因此,Union1004
从未重新评估计划节点 5 中的引用。我看到你现在在这里报告了它https://feedback.azure.com/forums/908035-sql-server/suggestions/39428632-microsoft-sql-server-2014-incorrect-result-when-s
添加查询提示
OPTION (MERGE UNION)
适用于您的示例,我不知道这是否足以在所有情况下避免该错误,但从链接的 Paul White 答案看来它应该可以工作。在您的示例中,它的工作原理是排序在计划中被向下推,因此它只会倒回TestCaseTemp
行,而不是整个联合结果。您还可以添加适当的索引以完全删除排序。这是 SQL Server 决定是否需要对某些计划形状进行重新绑定的方式中的一个错误。自 SQL Server 2005 以来,它一直在产品中。
最小复制
这使用一个跟踪标志来强制一个假脱机:
计划:
错误的结果:
扩展复制
这不需要跟踪标志来生成假脱机:
错误状态
已针对 Azure SQL 数据库修复。还从 SQL Server 2019 CU9、SQL Server 2017 CU23 和 2016 SP2 CU16 修复。
KB5000649 - 修复:由于未检测到标量表达式的串联参数导致错误结果