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 楼
初步查看执行计划后,发现表达式1/0
是在Compute Scalar运算符中定义的:现在,即使执行计划确实从最左侧开始执行,也要迭代调用
Open
和GetRow
方法子迭代器返回结果,SQL Server 2005和更高版本包含一种优化,该表达式通常仅由Compute Scalar定义表达式,并将评估推迟到随后的操作需要结果时:在这种情况下,仅当组装要返回给客户的行时才需要表达式结果(您可以认为这发生在绿色的
SELECT
图标上)。按照这种逻辑,延迟求值将意味着永远不会对表达式求值,因为两个计划都不会生成返回行。稍微花点时间,聚簇索引搜索和表扫描都不会返回一行,因此没有要汇编的行可以返回给客户端。但是,有一个单独的优化可以识别一些表达式作为运行时常量,因此在查询执行开始之前进行一次评估。
在这种情况下*,可以在showplan XML(左侧的“聚集索引搜索”计划,右侧的“表扫描”计划)中找到已发生的指示:
在此博客文章中,我写了更多有关底层机制以及它们如何影响性能的内容。使用那里提供的信息,我们可以修改第一个查询,以便在执行开始之前对两个表达式进行求值和缓存:
select 1/0 * CONVERT(integer, @@DBTS)
from #temp
where id = 1
select 1/0
from #temp2
where id = 1
现在,第一个计划还包含一个常量表达式引用,并且两个查询都产生错误消息。第一个查询的XML包含:
更多信息:计算标量,表达式和性能
由于运行时常量缓存而报告错误的示例,但存在执行计划中没有说明(图形或XML):
SELECT TOP (1) id
FROM #temp2
WHERE id = 1
ORDER BY 1/0;
#2 楼
我将明智地猜测(并且在此过程中可能会吸引SQL Server专家,他可能会给出非常详细的答案)。第一个查询将执行方式如下:
扫描主键索引
在查询中查找数据表中的值
之所以选择此路径,是因为您在主键上有一个
where
子句。它永远不会进入第二步,因此查询不会失败。第二步没有主键可以运行,因此它的查询方式为:
对数据进行全表扫描并检索必要的值
其中一个值是引起问题的
1/0
。这是SQL的示例服务器优化查询。在大多数情况下,这是一件好事。 SQL Server会将条件从
select
移到表扫描操作中。这通常可以节省查询评估的步骤。但是,这种优化并不是一件好事。实际上,这似乎违反了SQL Server文档本身,该文档指出
where
子句先于select
进行求值。好吧,他们可能对此有一些博学的解释。但是,对于大多数人而言,在where
之前进行逻辑处理select
意味着(除其他事项外)“不会在未返回用户的行上生成select
-子句错误”。
评论
如果您是对的话,+ 1毫无头绪,但是鉴于唯一的区别是主键,我可以看到最佳答案。
–第十二
14年2月13日在18:49
@GordonLinoff保罗·兰德尔(Paul Randal)刚刚在Twitter上确认您的回覆不胜枚举。
–SchmitzIT
2014年2月13日在19:12
@Still,实际的执行顺序(无论如何)不应导致类似的错误消息。
–超立方体ᵀᴹ
2014年2月13日21:00
@ypercube Erland Sommarskog会同意你的观点(连接项)
–保罗·怀特♦
2014年2月13日在21:04
谢谢您的指点-我已登录并支持该请求。
–戈登·利诺夫(Gordon Linoff)
14年2月13日在21:08