为什么SQL Server在仍然执行查询的同时返回一些行,有时却不返回?


33

在某些查询中,当我们单击“执行”时,它显示了一些行并且还在不断增长,但是查询尚未结束。但有时,它会一直等到查询结束。

为什么会这样?有办法控制吗?

Answers:


43

与往常一样(大多数情况下),答案在于执行计划。

某些运算符要求所有行都可以到达它们,然后才能开始处理这些行并将它们传递到下游,例如:

  • 哈希联接(关于建立哈希表)
  • 哈希匹配
  • 排序(散列流除外)

因此,它们要么被称为阻塞操作,要么被称为走走停停的运算符,当优化器认为必须处理大量数据才能找到您的数据时,通常会选择它们。

还有其他运算符可以开始流式处理,或立即传递所有找到的行

  • 嵌套循环
  • 支持索引的合并联接
  • 流聚合

当查询立即开始返回数据,但又没有立即完成时,通常表明优化器选择了使用启动成本较低的运算符快速定位并返回某些行的计划。

发生这种情况的原因可能是您或优化器引入了行目标。

如果出于某种原因(缺少SARGability,参数嗅探,统计信息不足等)而选择了错误的计划,也会发生这种情况,但这需要更多的挖掘工作才能弄清楚。

有关更多信息,请在此处查看Rob Farley的博客

保罗·怀特(Paul White)在这里这里这里这里关于连续目标的系列。

还应该注意的是,如果您在谈论SSMS,则只有在整个缓冲区被填满后才出现行,而不仅仅是故意的。


14

如果我了解您的观察,这就是Management Studio 呈现行的方式,与SQL Server 返回行的方式无关。实际上,通常当您将较大的结果返回给SSMS并尝试在网格中呈现它们时,SSMS无法跟上,并且SQL Server最终等待应用程序处理更多行。在这种情况下,您将看到SQL Server累积ASYNC_NETWORK_IO等待。

您可以通过使用“结果到文本”而不​​是“结果到网格”来进行某种程度的控制,因为SSMS绘制文本的速度比绘制网格更快,但是您可能会发现,这可能会影响可读性,具体取决于列数和所涉及的数据类型。当SSMS决定将结果实际写到该窗格时,这两者都会受到影响,这取决于输出缓冲区的容量。

如果有多个语句,并且要强制缓冲区将输出结果呈现到消息窗格,则可以在语句之间使用一些打印技巧:

RAISERROR('', 0, 1) WITH NOWAIT;

但是,当您试图让SSMS在所有输出均来自单个语句时更快地渲染行时,这将无济于事。

更直接地,您可以通过限制要在SSMS中呈现的结果数来控制它。我经常看到人们抱怨将一百万行返回到网格需要多长时间。到底谁会在SSMS网格中处理一百万行呢,我不知道。

有一些类似的hack OPTION (FAST 100),它们会优化以检索前100行(或如果没有outer的话则是任何100行ORDER BY),但是这样做的代价是检索其余行的速度要慢得多,而计划则更多整体上效率低下,所以恕我直言,这并不是一个不错的选择。


1

您的问题不是关于SQLServer本身,而是:

  • SQL服务器
  • 网络
  • SSMS作为客户端应用程序

有办法控制吗?

简短答案

  1. 尝试sqlcmd代替ssmssqlcmd-mode ofssms
  2. 检查您的连接和会话设置

长答案

当然!但不是一个 -概率

  1. 在ssms sqlcmd中使用sqlcmd-mode 或以-mode 执行查询。
  2. 如果要排除网络角色,请在具有共享内存连接的服务器上运行查询。
  3. 如果即使使用共享内存连接也无法满足查询性能,请分析执行计划。如果查询无法通过网络执行-请致电您的网络网络管理员以寻求帮助。如果您的查询仅在SSMS中执行不佳-请进一步阅读。
  4. 现在我们确定问题出在客户端(在本例中为ssms)。查看SSMS中的连接和会话设置。不相信ssms接口并使用SQL Profiler进行检查:查找连接的依据,spid然后您将获得会话设置的完整列表。与sqlcmd会话设置进行比较。如果没有点击-将所有会话设置从事件探查器复制到查询脚本中,以sqlcmd-mode 执行并逐步切换设置,您将发现罪魁祸首。

祝好运!


-2

要添加到sp_BlitzErik的答案中,请使用NOT IN ()带有sub选择的示例。为了确定某个项目是否在嵌套查询的结果中,(通常)有必要检索整个结果。

因此,我发现提高此类查询性能的一种简便方法是将其重写为LEFT OUTER JOINwith的条件为RIGHTnull的条件(当然,您可以翻转它,但是谁使用RIGHT OUTER JOINS?)。这样可以使结果立即开始返回。


我不这么认为。如果比较的列不可为空,则对于3个版本的抗连接(不输入,不存在,左连接/为空)的结果应该是相同的,计划也通常是相同的。不必检索整个结果。
ypercubeᵀᴹ

如果子查询是一个非常复杂的查询,则生成的查询需要先检查整个子查询,然后再检查NOT IN条件WHERE t.x IN (<complex SELECT subquery>),即等效的LEFT JOIN LEFT JOIN (<complex SELECT subquery>) AS r ON r.x = t.x .... WHERE r.x IS NULL,,那么子查询也必须进行求值(因此,与NOT相同的复杂计划IN版本)。
ypercubeᵀᴹ

@ypercubeᵀᴹ过去对我有用。我已经看到查询从花费几分钟到返回亚秒级。
JimmyJames

@ypercubeᵀᴹ我在Oracle中整理了一个简单的示例(很抱歉,目前我无法访问SQLServer),他们肯定有不同的解释计划。也许它们不是有意义的差异,但看起来却完全不同。
JimmyJames

@JimmyJames:如果要稳定的性能,这不是一个简单的方法,并且这种“优化”对于SQLServer版本非常敏感。而且,请不要误以为吸引Oracle(哪个版本?)。从历史上看,SQLServer是首选,NOT EXISTS但Oracle NOT IN在查询中。但是今天,它必须被视为计划生成器中的错误
Alex Yu,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.