在 MS SQL Server 2012 中运行以下查询时,第二个查询失败但第一个查询失败。此外,在没有 where 子句的情况下运行时,两个查询都将失败。我不知道为什么两者都会失败,因为两者都应该有空的结果集。任何帮助/见解表示赞赏。
create table #temp
(id int primary key)
create table #temp2
(id int)
select 1/0
from #temp
where id = 1
select 1/0
from #temp2
where id = 1
对执行计划的初步了解表明,表达式
1/0
是在 Compute Scalar 运算符中定义的:现在,即使执行计划确实从最左边开始执行,在子迭代器上迭代调用
Open
和GetRow
方法以返回结果,SQL Server 2005 及更高版本包含一个优化,即表达式通常仅由计算标量定义,评估推迟到后续操作需要结果:在这种情况下,只有在组装行返回客户端时才需要表达式结果
SELECT
(您可以认为发生在绿色图标处)。按照这种逻辑,延迟评估意味着永远不会评估表达式,因为这两个计划都不会生成返回行。稍微强调一下,聚簇索引查找和表扫描都没有返回行,因此没有行可组合以返回给客户端。但是,有一个单独的优化,其中一些表达式可以被识别为运行时常量,因此在查询执行开始之前评估一次。
在这种情况下*,可以在 showplan XML(左侧的 Clustered Index Seek 计划,右侧的 Table Scan 计划)中找到已发生这种情况的指示:
我在这篇博文中写了更多关于底层机制以及它们如何影响性能的内容。使用那里提供的信息,我们可以修改第一个查询,以便在执行开始之前评估和缓存两个表达式:
现在,第一个计划还包含一个常量表达式引用,并且两个查询都会产生错误消息。第一个查询的 XML 包含:
更多信息:计算标量、表达式和性能
由于运行时常量缓存而报告错误但执行计划(图形或 XML)中没有指示的示例:
我将明智地猜测(并且在此过程中可能会吸引 SQL Server 专家,他可能会给出非常详细的答案)。
第一个查询执行如下:
它选择此路径是因为您
where
在主键上有一个子句。它永远不会进入第二步,因此查询不会失败。第二个没有要运行的主键,因此它将查询处理为:
这些价值观之一是
1/0
导致问题的原因。这是 SQL Server 优化查询的示例。在大多数情况下,这是一件好事。SQL Server 会将条件从移动
select
到表扫描操作中。这通常可以节省查询评估的步骤。但是,这种优化并不是一件绝对的好事。事实上,它似乎违反了 SQL Server文档本身,该文档说该
where
子句在select
. 好吧,他们可能对这意味着什么有一些博学的解释。但是,对于大多数人来说,在逻辑上处理where
beforeselect
将意味着(除其他外)“不要select
在未返回给用户的行上生成 -clause 错误”。