添加联接提示时,为什么SQL Server行估计会更改?


15

我有一个查询,该查询连接了几个表并且执行得很差-行估计相差1000倍,并且选择了嵌套循环连接,从而导致了多个表扫描。查询的形状非常简单,看起来像这样:

SELECT t1.id
FROM t1
INNER JOIN t2 ON t1.id = t2.t1_id
LEFT OUTER JOIN t3 ON t2.id = t3.t2_id
LEFT OUTER JOIN t4 ON t3.t4_id = t4.id 
WHERE t4.id = some_GUID

在处理查询时,我注意到当我提示它对一个连接使用合并连接时,它的运行速度快了很多倍。我能理解-合并联接是联接数据的更好选择,但是SQL Server选择嵌套循环时并不能正确估计它。

我不完全理解的是,为什么此联接提示会更改所有计划运营商的所有估计?通过阅读不同的文章和书籍,我假设基数估计是在构建计划之前执行的,因此使用提示不会改变估计,而是明确地告诉SQL Server使用特定的物理联接实现。

但是,我看到的是,合并提示使所有估计变得非常完美。为什么会发生这种情况,并且有任何通用的技术可以使查询优化器在没有提示的情况下做出更好的估计-考虑到统计数据显然可以做到这一点?

UPD:可在此处找到匿名的执行计划:https ://www.dropbox.com/s/hchfuru35qqj89s/merge_join.sqlplan?dl =0 https://www.dropbox.com/s/38sjtv0t7vjjfdp/no_hints_join.sqlplan?dl = 0

我使用TF 3604、9292和9204检查了两个查询使用的统计信息,它们是相同的。但是,查询之间扫描/查找的索引有所不同。

除此之外,我尝试使用来运行查询OPTION (FORCE ORDER)-它的运行速度比使用合并连接还要快,为每个连接选择HASH MATCH。


3
您是否注意到您有外部联接,但随后在where子句中使用该表?
James Z

@JamesZ-是的,我知道这一点,但是我认为这没有问题。
Alexander Shelemin 2015年

9
@AlexSh嗯,这有一个逻辑/语义问题,因为这会将您的外部联接更改为内部联接。
亚伦·伯特兰

Answers:


21

通过阅读不同的文章和书籍,我假设基数估算是在计划制定之前进行的。

不完全是。一个初始基数估计导出,从而影响由所述优化器选择的初始连接顺序(简化和其他工作之后)。

但是,随后的探索(在基于成本的优化期间)可以并且经常确实导致计算出新的基数估计。这些后来的CE或多或少是“准确的”。如果结果被低估,优化器可能会选择一个看起来更便宜的计划,但实际上会运行更长的时间。

通常,不能保证语义相同的子树的基数估计会产生相同的结果。毕竟,这是一个统计过程,某些操作比其他操作具有更深入的CE支持。

在您的情况下,似乎还有另一个因素-优化器引入(或在顶部移动)顶部,该顶部在其下面的子树上设置行目标

计划片段

如果要启用跟踪标记4138(在2008 R2或更高版本上),则可能会发现估计值与预期更一致,甚至可能是优化器不再选择嵌套循环。

但是,我看到的是,合并提示使所有估计变得非常完美。

这里涉及到运气。人们倾向于以他们希望物理执行查询的顺序来编写查询,或者至少是联接。使用连接提示会附带一个FORCE ORDER,从而固定连接顺序以匹配文本格式,并关闭许多可能导致基数重新估计的优化程序探索规则。

除此之外,我尝试使用来运行查询OPTION (FORCE ORDER)-它的运行速度比使用合并连接还要快,为每个连接选择HASH MATCH。

这与提示联接相同,但不限制物理联接运算符的选择。同样,如果您碰巧以合理的方式编写查询联接顺序,则很有可能会得到一个合理的计划。当然,您会错过这种优化器的许多功能,这在更一般的情况下可能不会产生最佳结果。

您可能不希望FORCE ORDER经常使用它,因为它是一个非常强大的提示(伪指令),其作用比简单地强制联接的顺序要广泛。例如,它可以防止优化程序四处移动聚集并引入局部聚集。我非常建议您不要使用此提示,除非在非常特殊的情况下以及真正的专家调谐器。

详细分析将需要比我现在更多的时间,并且需要访问仅统计数据的数据库副本。


-10

否定左侧的位置
为什么使优化器难以使用?
在3个或更多的连接处,优化器将
趋向于防御并进入循环连接,以保护内存一个或一个连接中的条件也将趋于进入循环连接-我是否有确凿的证据表明每次都会发生-不-仍然是现实
通过多个联接,您可以将条件从何处拉到联接中

SELECT t1.id
  FROM t1
  JOIN t2 
        ON t1.id = t2.t1_id
  JOIN t3 
        ON t2.id = t3.t2_id
  JOIN t4 
        ON t3.t4_id = t4.id 
       AND t4.id = some_GUID 

甚至更好-我敢打赌,这将满足或击败您的提示或强迫

SELECT t1.id
  FROM t1
  JOIN t2 
        ON t1.id = t2.t1_id
  JOIN t3 
        ON t2.id = t3.t2_id
       AND t3.t4_id = some_GUID

提示的问题在于它们用于特定状态的数据。编写一个干净的查询,让优化器完成其工作。有时它只需要更多的统计信息即可执行正确的操作,但随后它将锁定。

为什么估算不同。不同的计划。从为优化器提供战斗机会的查询开始。

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.