Answers:
初步查看执行计划,可以发现该表达式1/0
是在Compute Scalar运算符中定义的:
现在,即使执行计划就开始在最左边的执行,反复呼吁Open
和GetRow
对儿童的迭代方法返回的结果,SQL Server 2005和更高版本中包含借此表达往往只优化定义由计算标量,以推迟到后续评估操作需要结果:
在这种情况下,表达式 仅当组装要返回给客户的行时才需要结果(您可以认为这发生在绿色SELECT
图标上)。按照这种逻辑,延迟求值将意味着永远不会对表达式求值,因为两个计划都不会生成返回行。稍微花点时间,聚簇索引查找和表扫描都不会返回一行,因此没有要汇编的行可以返回给客户端。
但是,有一个单独的优化,可以将某些表达式标识为运行时常量,并在查询执行开始之前对其进行一次评估。在这种情况下,可以在showplan XML(左侧的“聚集索引查找”计划,右侧的“表扫描”计划)中找到已发生的指示:
我在此博客文章中写了更多有关底层机制以及它们如何影响性能的文章。。使用此处提供的信息,我们可以修改第一个查询,以便在执行开始之前对两个表达式进行求值和缓存:
select 1/0 * CONVERT(integer, @@DBTS)
from #temp
where id = 1
select 1/0
from #temp2
where id = 1
现在,第一个计划还包含一个常量表达式引用,并且两个查询都生成错误消息。第一个查询的XML包含:
详细信息:计算标量,表达式和性能
我将明智地猜测(并且在此过程中可能会吸引SQL Server专家,他可能会给出非常详细的答案)。
第一个查询的执行方式如下:
它选择此路径是因为您where
在主键上有一个子句。它永远不会进入第二步,因此查询不会失败。
第二个没有主键可运行,因此它以如下方式处理查询:
这些值之一1/0
导致了问题。
这是SQL Server优化查询的示例。在大多数情况下,这是一件好事。SQL Server将条件从select
移到表扫描操作中。这通常可以节省查询评估的步骤。
但是,这种优化并不是一件好事。实际上,这似乎违反了SQL Server 文档本身,该文档指出该where
子句在之前进行评估select
。好吧,他们可能对此有一些博学的解释。但是,对于大多数人而言,逻辑上处理where
before select
意味着(除其他事项外)“不会select
在未返回给用户的行上产生子句错误”。