强制加入顺序

Gre*_*reg 6 performance sql-server-2005 join sql-server

我有一个超过一百万行的表。这些行可以在同一个表中有一个父记录,通过在 6 个不同的列上连接到它自己(即没有单列ParentID)。根据这些连接,每个孩子都恰好有 1 个父级,并且每个记录要么是父级记录,要么是子级记录(即没有祖父级记录)。

SELECT  *
FROM    TheTable AS ChildRecords
JOIN    TheTable AS ParentRecords
ON      ChildRecords.Column1 = ParentRecords.Column1
AND     ChildRecords.Column2 = ParentRecords.Column2
AND     ChildRecords.Column3 = ParentRecords.Column3
AND     ChildRecords.Column4 = ParentRecords.Column4
AND     ChildRecords.Column5 = ParentRecords.Column5
AND     ChildRecords.Column10 = ParentRecords.Column6
Run Code Online (Sandbox Code Playgroud)

注意第 10 列连接到第 6 列,但该列本身没有找到唯一的父级 - 可能有多个带有column10= 的“父级” column6

这通常工作正常,但是如果我们将它作为更大查询的一部分,SQL Server 通常会先尝试解析此连接,然后再解析其他连接。当它处于 CTE 或加入 CTE 时尤其如此。它通常是查询计划中发生的第一个连接。这通常会导致数以万计的连接,然后再过滤到我感兴趣的 100 条左右的记录。发生这种情况时,查询需要几分钟才能运行。

我注意到我可以通过使其成为左连接来影响查询计划。这是有道理的,因为如果它是左联接,那么 SQL Server 不知道每个孩子都有 1 个父级,因此它总是必须首先找到子级记录。

SELECT  *
FROM    TheTable AS ChildRecords
LEFT JOIN TheTable AS ParentRecords
ON      ChildRecords.Column1 = ParentRecords.Column1
AND     ChildRecords.Column2 = ParentRecords.Column2
AND     ChildRecords.Column3 = ParentRecords.Column3
AND     ChildRecords.Column4 = ParentRecords.Column4
AND     ChildRecords.Column5 = ParentRecords.Column5
AND     ChildRecords.Column10 = ParentRecords.Column6
Run Code Online (Sandbox Code Playgroud)

当它以这种方式运行查询时,它将时间从几分钟减少到 < 2 秒。

因为每个孩子都有一个父级,左联接给出与内联接相同的结果,但是感觉不对——它应该是内联接。

我已经检查过这个表上的索引设置是否正确,我已经尝试添加和编辑我们拥有的索引,但它不会改变查询计划。时间似乎是因为它正在查询获得每个孩子/父母组合,然后才将其限制为我想要的组合。

我可以强制 SQL Server 按照我指定的顺序运行连接,而不是让它尝试重新排序查询吗?

Gre*_*reg 4

看来我可以在查询末尾指定 OPTION (FORCE ORDER),这将使连接以正确的顺序发生。有很多人警告不要这样做,因为它会阻止 SQL 优化我的查询,所以我肯定会谨慎使用它(并密切关注这些查询)。

  • 我过去使用过强制订单,将来可能会再次使用它。是的,它带有警告,是的,您应该在使用它的任何过程或查询中放置相同的警告。就像任何其他技巧/提示/晦涩的优化一样,如果你不这样做,[cargo cult](http://en.wikipedia.org/wiki/Cargo_cult_programming)应用程序往往会遵循。反对这种方法(这是一种有效/好的方法)的论点是,如果数据分布发生变化以支持替代计划,那么您就完蛋了。记住这一点。 (4认同)
  • 当优化器放弃并说“对不起”时,我使用强制订单来制定计划!是的,查询是令人厌恶的,但强制排序会大大缩小搜索空间,以至于优化器可以产生有用的东西。该选项有其用途。 (3认同)