Ale*_*sko 5 sql-server sql-server-2017 query-performance
我有一个带有分组的简单查询,运行良好,直到我添加了另一个连接:
select
[ca].Value,
[c].cID,
[c].Name
from ReportingDB..Table1 [t1]
join MainDB..Companies [c] on
[t1].CompanyID = [c].cID
and [c].cID not in (1)
join MainDB..CompanyAttributes [ca] on -- this is the join that causes trouble
[t1].CompanyID = caCID
and caAttr = 26
group by [ca].Value, [c].cID, [c].Name
Run Code Online (Sandbox Code Playgroud)
信息:
Companies
表是一个“查找”表,有 2254 行,cID
PK与表
CompanyAttributes
有多对一关系Companies
,有 4055 行
Table1
,与表有多对一关系Companies
,有 3,485,150 行,
估计的执行计划看起来并不异常。
sys.dm_exec_requests
,对于运行查询的会话,wait_type 为 NULL,cpu_time
并且logical_reads
继续增长原始查询运行超过 1 小时而未完成的根本原因是什么?
我解决了性能瓶颈本身(请参阅我的答案),但不明白到底是什么导致原始查询运行 1 小时并且无法在像样的服务器上完成,查询的表并不大。预计原始查询会在 1 分钟内完成。
显示的估计执行计划的主要问题是表 1 的聚集索引扫描上方的 Top 运算符。扫描有一个残差谓词:
[ReportingDB].[dbo].[Table1].[CompanyID]=[MainDB].[dbo].[CompanyAttributes].[cacID]
Run Code Online (Sandbox Code Playgroud)
优化器尝试估计在通过测试之前需要从扫描中读取多少行。它使用的逻辑是通用的,在我看来并不是特别合理。特别是,如果没有匹配项,扫描将运行至完成,检查所有 3,514,200 行。
更重要的是,此扫描将对公司属性扫描返回的每一行重复,乘以公司搜索返回的行数。这就是嵌套循环连接的工作原理。
优化器对于在 Table1 的每次扫描中查找匹配项非常乐观。这导致该计划形状在所考虑的替代方案中具有最低的估计成本。其根本原因是 Top 运算符引入的行目标。
如果您对顶部的来源感到好奇(它不在您的查询文本中),请查看我的密切相关文章“行目标,第 4 部分:反连接反模式”。
简而言之:优化器引入了本地(部分)聚合作为其计划搜索的一部分。该聚合结果在逻辑上是多余的,并被替换为等效的 Top。Top 的一个不幸的副作用是引入行目标,从而显着降低扫描的估计成本。
带有剩余谓词的扫描之上的顶部(尤其是重复扫描的情况)是非常需要警惕的反模式。