TOP如何(以及为什么)影响执行计划?


35

对于我要优化的中等复杂查询,我注意到删除该TOP n子句会更改执行计划。我可能已经猜到,当查询中包含TOP n数据库引擎时,该查询将忽略该TOP子句而运行,然后最后仅将结果集缩减为所请求的n行。图形化的执行计划似乎表明是这种情况,这是TOP“最后一步”。但似乎还有更多的事情正在进行。

我的问题是,TOP n子句如何(以及为什么)影响查询的执行计划?

这是我的情况的简化版本:

该查询匹配两个表A和B中的行。

如果没有该TOP子句,优化器估计表A将有19k行,表B将有46k行。对于A,返回的实际行数是16k,对于B,返回的行数是13k。散列匹配用于将两个结果集连接到a总共69行(然后应用排序)。这个查询很快发生。

当我添加TOP 1001优化器时,不使用哈希匹配;相反,它首先对表A的结果进行排序(相同的估计值/实际值为19k / 16k),并针对表B进行嵌套循环。表B的估计行数现在为1,奇怪的是,TOP n直接影响表B 针对B的估计执行次数(索引查找)-始终为2n + 1,在我的情况下为2003。如果我更改,则此估计值也会相应更改TOP n。当然,由于这是嵌套联接,因此实际执行次数为16k(表A中的行数),这会使查询速度变慢。

实际情况要复杂一些,但这捕获了基本思想/行为。使用索引查找来搜索两个表。这是SQL Server 2008 R2企业版。


该查询具有一个ORDER BY子句。TOP在计划中的这种地方添加更改,但是我更担心它如何影响针对表B的索引查找的执行次数……(当然,这可能是相关的-我不知道)
大卫

1
相关讨论:FAST num_rows查询提示。
Remus Rusanu 2012年

Answers:


38

我猜想当查询包含TOP n时,数据库引擎将忽略TOP子句运行查询,然后最后仅将结果集缩减为所请求的n行。图形化的执行计划似乎表明是这种情况-TOP是“最后”一步。但似乎还有更多的事情正在进行。

上面所说的方式使我认为您可能对查询的执行方式有不正确的看法。查询计划中的运算符不是步骤(上一步的完整结果集由下一个步骤评估。

SQL Server使用流水线执行模型,其中每个运算符都公开Init()GetRow()Close()之类的方法。就像GetRow()名称所暗示的那样,运算符按需一次生成一行(根据其父运算符的要求)。在联机丛书“ 逻辑和物理操作员”参考中对此进行了记录,在我的博客文章“ 为什么查询计划向后运行”中有更详细的介绍。此一次行模型对于形成查询执行的良好直觉至关重要。

我的问题是,TOPn子句如何(以及为什么)影响查询的执行计划?

某些逻辑操作(例如TOP,半联接和FAST n 查询提示)会影响查询优化器对执行计划替代方案的成本。基本思想是,一个可能的计划形状可能比优化返回所有行的另一个计划更快地返回前n行。

例如,索引散列循环联接通常是返回少量行的最快方法,尽管散列或合并联接与扫描在更大的集合上可能更有效。查询优化器做出这些选择的原因是通过在操作的逻辑树中的特定点设置行目标

行目标修改了查询计划替代方案的成本计算方式。它的本质是,优化器首先使每个运算符都花费成本,好像需要整个结果集一样,然后在适当的位置设置行目标,然后向下推回计划树以估计期望检查的行数达到行目标。

例如,逻辑TOP(10)在逻辑查询树中的特定点将行目标设置为10。修改达到行目标的操作员成本,以估算他们需要生产多少行才能达到行目标。这种计算可能会变得很复杂,因此,通过一个完整的示例和带有注释的执行计划,可以更轻松地理解所有这些内容。行目标的影响不仅限于连接类型的选择,也不是扫描优先选择查找和查找。更多细节在这里

与往常一样,基于行目标选择的执行计划将受优化器的推理能力和提供给它的信息质量的影响。在实践中,并非每个具有行目标的计划都会更快地产生所需的行数,但是根据成本模型,它会产生。

在行目标计划证明不是更快的情况下,通常可以通过多种方式修改查询或向优化程序提供更好的信息,以使自然选择的计划最佳。哪种情况适合您,取决于具体的课程。行目标功能通常非常有效(尽管在并行执行计划中使用时需要注意一些错误)。

您的特定查询和计划可能不适用于此处的详细分析(如果您愿意,请务必提供实际的执行计划),但希望此处概述的想法将使您取得进展。


12

使用TOP时,优化程序会发现减少工作量的机会。如果您要求10行,那么很有可能不需要消耗整个数据集。因此,可以将TOP运算符向右推得更远。它将继续从下一个运算符(在其右侧)请求行,直到收到足够的行为止。

您指出,没有TOP,查询将在最后对数据进行排序。如果引擎可以预先知道该联接要满足多少行,则可以选择使用类似的计划,将TOP放在左侧。但是,由于进行哈希匹配的工作量相对较高,并且可能没有合并联接的选择,因此优化程序可能更喜欢在右侧进一步过滤TOP。

查询表B时,它一次只获取一行。这就是估计值是1的原因。它还假定只会在50%的时间内找到该行。因此,它猜测需要2n + 1次搜索才能找到它。


估计行数会根据获取数据的方式而变化似乎并不正确。它如何获取数据不应影响基数。相反,它获取方式的改变会反映在执行次数上,对吗?
大卫

“估计的行数”是每次执行。在嵌套循环中,很可能执行不止一次。
Rob Farley 2012年

这与实际的行数和执行次数(实际)不同。如果实际计划显示了16,834次实际执行,并且返回了15,407条实际行,我认为这意味着它执行了16k次查找,但仅发现与谓词匹配的15k次。如果这意味着每执行15,000行,则将为15k * 16k = 2.4亿行-比表大10倍...
David

另外,我不确定我是否遵循您答案的最后声明。当您说2n + 1试图找到“它”时,您说“它”是什么意思?当然不是一行吗?您是否意味着优化器假设A中的任何给定行都将有50%的机会与B匹配,因此它需要从A中“尝试” 2003行以从B中获得1001个匹配项?Microsoft是否在任何地方记录了此行为?它与该TOP子句有什么关系?感谢您的答复/耐心等待。
大卫

是的,估计行是每次执行。实际行是总计。尽管操作员返回的行多于表中的行没有问题,因为很容易演示操作员多次返回同一行。
罗布·法利
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.