为什么在 SQL Server 2012 中查询结果集为空会出错?

Dav*_*idN 33 sql-server sql-server-2012

在 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
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 41

对执行计划的初步查看表明该表达式1/0是在 Compute Scalar 运算符中定义的:

图解计划

现在,即使执行计划就开始在最左边的执行,反复呼吁OpenGetRow对儿童的迭代方法返回的结果,SQL Server 2005和更高版本中包含借此表达往往只优化定义由计算标量,以推迟到后续评估操作需要结果

出现在由 SET STATISTICS XML 生成的 Showplans 中的计算标量运算符可能不包含 RunTimeInformation 元素。 在图形显示计划中,当在 SQL Server Management Studio 中选择“包括实际执行计划”选项时,“属性”窗口中可能不显示“实际行”、“实际重新绑定”和“实际回滚”。 发生这种情况时,意味着虽然这些运算符用于编译的查询计划中,但它们的工作由运行时查询计划中的其他运算符执行。 另请注意,SET STATISTICS PROFILE 生成的 Showplan 输出中的执行次数等于 SET STATISTICS XML 生成的 Showplan 中重新绑定和倒带的总和。 来自:MSDN 联机丛书

在这种情况下,只有在组装行返回给客户端时才需要表达式结果(您可以认为发生在绿色SELECT图标处)。按照这种逻辑,延迟评估意味着永远不会评估表达式,因为这两个计划都不会生成返回行。稍微说明一下,Clustered Index Seek 和 Table Scan 都不会返回一行,因此没有要组装返回给客户端的行。

然而,有一个单独的优化,其中一些表达式可以被识别为运行时常量,因此在查询执行开始之前评估一次

在这种情况下*,可以在 showplan XML 中找到已发生的指示(左侧的聚集索引查找计划,右侧的表扫描计划):

展览计划 XML

在这篇博文中写了更多关于底层机制以及它们如何影响性能的文章。使用那里提供的信息,我们可以修改第一个查询,以便在执行开始之前评估和缓存两个表达式:

select 1/0 * CONVERT(integer, @@DBTS)
from #temp
where id = 1

select 1/0
from #temp2
where id = 1
Run Code Online (Sandbox Code Playgroud)

现在,第一个计划还包含一个常量表达式引用,并且两个查询都会产生错误消息。第一个查询的 XML 包含:

常量表达式

更多信息:计算标量、表达式和性能


由于运行时常量缓存而报告错误的示例,但执行计划中没有指示(图形或 XML):

SELECT TOP (1) id
FROM #temp2
WHERE id = 1
ORDER BY 1/0;
Run Code Online (Sandbox Code Playgroud)

神秘计划


Gor*_*off 22

我会聪明地猜测(并且在这个过程中可能会吸引一个 SQL Server 专家,他可能会给出一个非常详细的答案)。

第一个查询接近执行:

  1. 扫描主键索引
  2. 在数据表中查找查询所需的值

它选择这条路径是因为您where在主键上有一个子句。它永远不会进入第二步,因此查询不会失败。

第二个没有主键可以运行,所以它接近查询:

  1. 对数据进行全表扫描并检索必要的值

这些值之一1/0导致了问题。

这是 SQL Server 优化查询的示例。在大多数情况下,这是一件好事。SQL Server 将把条件从select转移到表扫描操作中。这通常会节省查询评估中的步骤。

但是,这种优化并不是一件绝对的好事。事实上,它似乎违反了 SQL Server文档本身,该文档说该where子句在select. 好吧,他们可能对这意味着什么有一些博学的解释。但是,对于大多数人来说,在逻辑上处理wherebeforeselect将意味着(除其他外)“不要select在未返回给用户的行上生成-clause 错误”。

  • @ypercube Erland Sommarskog 会[同意你](https://connect.microsoft.com/SQLServer/feedback/details/537419/sql-server-should-not-raise-illogical-errors)(连接项目) (7认同)
  • @Still,实际的执行顺序,无论多么不同,都不应该导致这样的错误消息。 (4认同)
  • 感谢您的指点——我登录并支持该请求。 (2认同)