我有以下查询:
Select TOP 5000
CdCl.SubId
From dbo.PanelCdCl CdCl WITH (NOLOCK)
Inner Join dbo.PanelHistory PH ON PH.SubId = CdCl.SubId
Where CdCl.PanelCdClStatusId IS NULL And PH.LastProcessNumber >= 1605
Order By CdCl.SubId
查询计划如下所示:
PanelCdCl和PanelHistory表在SubId上都有一个聚簇索引/主键,它是索引中唯一的列。每个表中的每个SubId只有一行。两个表的总行数约为35M。
我很好奇为什么查询计划在聚合索引列上完成连接时会在PanelHistory上显示聚簇索引扫描。
答案 0 :(得分:3)
它没有扫描PanelHistory的聚集索引(SubId)来查找SubId,它正在扫描它以找到LastProcessNumber >= 1605
的所有行。这是第一个合乎逻辑的步骤。
然后它同样扫描PanelCdCl以查找所有非空PanelCdClStatusId
行。然后,由于它们具有相同的索引(SubId),它们都已经在Join列上排序,因此它可以在没有其他排序的情况下执行Merge-Join。 (如果不必对输入行进行重新排序,Merge-Join几乎总是最有效的。)
然后它不必对ORDER BY进行排序,因为它已经在SubId
顺序。
最后,它执行TOP,它必须在其他所有内容之后(通过SQL子句逻辑执行顺序的规则)。
因此,它测试SubId
值的唯一位置是在Merge-Join中,它从不将其推送到扫描。如果它改为使用Hash-Join,这可能仍然适用。仅对于嵌套循环连接,它必须将SubId
测试作为搜索推送到表上,而这应该只是较低的分支,而不是较高的分支。
答案 1 :(得分:1)
合并连接运算符需要两个已排序的输入。两个表中的聚簇键为SubId
,这意味着PanelHistory
中的扫描将按正确顺序给出行。群集密钥包含在所有非群集密钥索引中,因此您将拥有NCI IX_PanelCdCl_PanelCdClStatusId
中的所有行,其中PanelCdClStatusId is null
由SubId
排序,因此也可以直接使用合并加入。
您在此处看到的实际上是两次扫描,PanelHistory
中的一个聚类键,LastProcessNumber > 1605
上的残差谓词和IX_PanelCdCl_PanelCdClStatusId
中的一个索引范围扫描,只要{{1} }}
然而,他们不会扫描整个表/索引。查询在查询计划中从左到右执行,其中PanelCdClStatusId is null
一次要求一行,直到不再有行为止。这意味着当顶级运算符具有所需的5000行时,它将停止从合并连接中请求新行。