Microsoft SQL Server 2014 从交叉应用中的查询嵌套

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 中的引用。

我看到你现在在这里报告了https://feedback.azure.com/forums/908035-sql-server/suggestions/39428632-microsoft-sql-server-2014-incorrect-result-when-s

是否有可能强制对每一行的嵌套查询进行评估?

添加查询提示OPTION (MERGE UNION)适用于您的示例,我不知道这是否足以避免所有情况下的错误,但从链接的 Paul White 答案来看,它似乎应该有效。在您的示例的情况下,它的工作原理是排序在计划中向下推,因此它只会倒带TestCaseTemp行,而不是整个合并结果。您还可以添加适当的索引以完全删除排序。


Pau*_*ite 5

这是 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 - 修复:由于未检测到来自标量表达式的连接参数而导致结果错误