Phi*_*0ne 10 sql-server union cross-apply
在 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
Run Code Online (Sandbox Code Playgroud)
结果:
| v | v2|
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
Run Code Online (Sandbox Code Playgroud)
测试案例 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;
Run Code Online (Sandbox Code Playgroud)
结果:
| v | v2|
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
Run Code Online (Sandbox Code Playgroud)
测试用例 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;
Run Code Online (Sandbox Code Playgroud)
结果:
| v | v2|
|---|---|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
Run Code Online (Sandbox Code Playgroud)
Mar*_*ith 10
这是 SQL Server 中的错误吗?
是的,当然,1
在最终结果的所有行中返回的 仅存在于外部输入的第一行中,因此甚至不应该在后续行的范围内。它看起来与 Paul White在这里详细讨论的基本问题相同。
我在 dbfiddle (SQL Server 2019) 中执行了您的最终查询并将计划粘贴到这里https://www.brentozar.com/pastetheplan/?id=Sy4sBB5lI 看起来排序执行了 4 次(每个外行一次)但是对于出于某种原因,它会倒带而不是重新绑定,因此不会多次调用该排序的子运算符。这是一个错误,因为对外部联接 ( Union1004
)的相关参数的引用应该会在其值发生更改时导致重新绑定。因此Union1004
,从未重新评估计划节点 5 中的引用。
是否有可能强制对每一行的嵌套查询进行评估?
添加查询提示OPTION (MERGE UNION)
适用于您的示例,我不知道这是否足以避免所有情况下的错误,但从链接的 Paul White 答案来看,它似乎应该有效。在您的示例的情况下,它的工作原理是排序在计划中向下推,因此它只会倒带TestCaseTemp
行,而不是整个合并结果。您还可以添加适当的索引以完全删除排序。
这是 SQL Server 决定某些计划形状是否需要重新绑定的方式中的一个错误。自 SQL Server 2005 以来,它一直在产品中。
这使用跟踪标志来强制假脱机:
CREATE TABLE #T (v integer NOT NULL);
INSERT #T VALUES (1), (2), (3), (4);
SELECT
#T.v,
A.*
FROM #T
CROSS APPLY
(
SELECT #T.v
UNION ALL
SELECT #T.v WHERE @@SPID < 0
) AS A
OPTION (QUERYTRACEON 8691);
Run Code Online (Sandbox Code Playgroud)
计划:
错误结果:
这不需要跟踪标志来生成线轴:
DROP TABLE #T;
CREATE TABLE #T (v integer NOT NULL);
INSERT #T
(v)
VALUES
(1), (1), (1), (1),
(1), (2), (3), (4);
SELECT
#T.v,
A.v
FROM #T
CROSS APPLY
(
SELECT v = MAX(U.v)
FROM
(
SELECT #T.v
UNION ALL
SELECT #T.v
WHERE @@SPID < 0
) AS U
GROUP BY U.v % 2
) AS A
ORDER BY #T.v
OPTION (USE HINT ('FORCE_DEFAULT_CARDINALITY_ESTIMATION'), HASH GROUP);
Run Code Online (Sandbox Code Playgroud)
已针对 Azure SQL 数据库修复。还从 SQL Server 2019 CU9、SQL Server 2017 CU23 和 2016 SP2 CU16 修复。
KB5000649 - 修复:由于未检测到来自标量表达式的连接参数而导致结果错误