为什么我的SQL Server 2017查询返回错误结果?

时间:2019-02-15 23:24:46

标签: sql-server sql-server-2017

以下是我遇到的问题的一些复制代码。

在SQL SERVER 2017中运行它,与任何其他SQL SERVER版本相比,您将获得不同(且不正确)的结果将数据库设置为在sql Server 2017实例上具有较低的兼容性级别,它也可以正常工作。

为什么会发生这种情况?如何在不更改兼容级别的情况下进行修复?

实际结果

for i in states:
        if i == "New York":
            dataset["New York"].replace("", 1)

预期结果

+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
| IsPriorAfter | IsIdealAfter | IsCurrentAfter | IsPrior | IsCurrent | IsIdeal | SecurityID | PosID |
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
|            1 |            1 |              1 |       1 |         1 |       1 |        123 |     1 |
|            0 |            0 |              0 |       0 |         1 |       1 |        234 |     2 |
|            0 |            0 |              0 |       1 |         0 |       0 |        234 |     3 |
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+

Repro

+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
| IsPriorAfter | IsIdealAfter | IsCurrentAfter | IsPrior | IsCurrent | IsIdeal | SecurityID | PosID |
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
|            1 |            1 |              1 |       1 |         1 |       1 |        123 |     1 |
|            0 |            1 |              1 |       0 |         1 |       1 |        234 |     2 |
|            1 |            0 |              0 |       1 |         0 |       0 |        234 |     3 |
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+

1 个答案:

答案 0 :(得分:2)

TLDR

这是a bug that has been fixed in CU8,因此至少安装该CU,最好安装最新的CU即可解决。

SQL Server 2017之前的版本

enter image description here

在SQL Server 2016中,计划如下所示。 INEXISTS的处理方式相同,因此它会评估以下三列。

   CASE WHEN IsPrior = 1   AND EXISTS (SELECT * FROM ForSubQuery WHERE SecID = MainTable.SecurityID) THEN 1 ELSE 0 END AS IsPriorAfter
   CASE WHEN IsIdeal = 1   AND EXISTS (SELECT * FROM ForSubQuery WHERE SecID = MainTable.SecurityID) THEN 1 ELSE 0 END AS IsIdealAfter
   CASE WHEN IsCurrent = 1 AND EXISTS (SELECT * FROM ForSubQuery WHERE SecID = MainTable.SecurityID) THEN 1 ELSE 0 END AS IsCurrentAfter

每个子查询实例在计划中都有其自己的运算符,并且查询返回正确的结果,但这是次优的,因为同一子查询每行最多可以执行多达次。

因为每个子查询旁边都有一个AND,但是如果该表达式的结果为false,SQL Server可以跳过对子查询的评估。这是通过每个包含通过谓词的嵌套循环来实现的。例如,与IsPriorAfter的求值相对应的一个具有IsFalseOrNull (IsPrior=1)的通过谓词

IsPrior=1是一个布尔表达式,可以返回falsenulltrue。然后IsFalseOrNull反转结果,并为1返回false,为null返回0。因此,如果true不是true(包括1)之外的任何事物,则通过谓词的评估结果为IsPrior / 1,然后跳过执行子查询。

SQL Server 2017 RTM

SQL Server 2017引入了新的优化规则CollapseIdenticalScalarSubquery。在RTM版本中,执行计划不正确。

问题计划

enter image description here

子查询现在在单个运算符中,并且通过谓词进行了组合

NULL

但是这种情况不正确!除非IsFalseOrNull([IsCurrent]=(1)) OR IsFalseOrNull([IsIdeal]=(1)) OR IsFalseOrNull([IsPrior]=(1)) trueIsPrior中的三个所有IsIdeal,否则它的值为IsCurrent

因此,在您的情况下,子查询仅执行一次(对于表的第一行-其中所有三列均等于1)。

对于其他两行,应执行但不执行。嵌套循环具有一个探针列,如果相关子查询返回一行,则该探针列设置为1。 (在计划中用1标记)。当跳过执行时,此探针列设置为Expr1016

计划中的最终计算标量具有以下表达式。当NULLExpr1016时,对于使用null计算的所有三个列,其求值为0

CASE

已修补SQL Server 2017

应用CU后的最终固定计划具有与2017 RTM计划相同的计划形状(子查询仅出现一次),但现在通过谓词

[Expr1005] = Scalar Operator(CASE WHEN [IsPrior]=(1) AND [Expr1016] THEN (1) ELSE (0) END), 
[Expr1009] = Scalar Operator(CASE WHEN [IsIdeal]=(1) AND [Expr1016] THEN (1) ELSE (0) END), 
[Expr1013] = Scalar Operator(CASE WHEN [IsCurrent]=(1) AND [Expr1016] THEN (1) ELSE (0) END)

仅当这些列中的的值为IsFalseOrNull([IsCurrent]=(1)) AND IsFalseOrNull([IsIdeal]=(1)) AND IsFalseOrNull([IsPrior]=(1)) 时,此评估结果才为true,因此现在可以在需要时准确地评估子查询。