两个CTE的内部连接需要很长时间

时间:2013-12-03 12:29:54

标签: sql sql-server performance join

我正在使用一个生产数据库,我需要将我的资源重新安排到多个CTE中,以便获得与我的数据匹配的正确格式。

这导致CTE1大约有7000条记录(需要几秒钟),CTE2大约有55000条记录(需要不到一秒钟)。

下一步是根据两列加入两个CTE。这一步花了19分钟,CTE2限制在TOP 1000!由于这些是CTE,因此没有一个指数。我的问题是如何在一个合适的时间内完成这场比赛(即使一两分钟就足够了)。

CTE1由四个字母数字列组成,最多包含8个字符。 CTE2由三个字母数字列组成,最多包含8个字符和两个日期时间。 inner joinCTE1的2个字母数字列与CTE2的2个字母数字列进行匹配。

示例CTE1(7000条记录)

A     | B     | C         | D
---------------------------------------------------------------------------
ABC   | 123   | 1234567   | 12345678
  • A有两个不同的值,匹配CTE2.A的不同值。
  • B有大约6500个区别。并非所有值都在CTE2.B
  • 中表示

示例CTE2(55000条记录)

A     | B     | C     | D                         | E
---------------------------------------------------------------------------
ABC   | 123   | XYZ   | 2013-10-11 15:00:00.000   | 2013-10-11 16:00:00.000
  • A有两个不同的值,匹配CTE1.A的不同值。
  • B有大约2000个区别。并非所有值(尽管大多数)都在CTE1.B
  • 中呈现

查询(使用此查询,CTE2的前1000名需要19分钟)

SELECT     CTE1.C
           ,CTE1.D
           ,CTE2.B
           ,CTE2.C
           ,CTE2.D
           ,CTE2.E
FROM       CTE1
INNER JOIN CTE2 ON CTE1.A = CTE2.A
                 AND CTE1.B = CTE2.B

3 个答案:

答案 0 :(得分:3)

这正在扩大Gareth的答案。

SQL Server不会单独执行CTE,然后在最终查询中将它们组合在一起。这是一个已知问题,并且有一个查询提示的请求会强制实现CTE的实现。您可以对请求here进行投票。

相反,它将CTE作为代码,将它们填充到查询中,然后“优化”整个事物。对于长而复杂的查询,可能会出错。

影响性能的最常见错误是将连接更改为嵌套循环连接,其中没有定义索引。您可以通过查看执行计划来查看是否发生了这种情况。如果它包含嵌套循环,那么您可能遇到麻烦。

如果是这样,请尝试使用OPTION (HASH JOIN, MERGE JOIN)运行查询以避免嵌套循环连接。

答案 1 :(得分:1)

问题在于,尽管你的CTE本身都表现良好,但当你加入它们时,你不只是说将CTE1的结果加入到CTE2的结果中,你加入了两个查询定义,所以每个都不是在连接在一起之前,SQL Server会自行执行,SQL Server会发现它认为是将这两个查询连接在一起的最佳方式,有时候,它不会经常导致异常且非最佳的执行计划。

有一个connect item requesting等同于CTE的NOEXPAND提示,因此您告诉优化者不要扩展定义。如果你不想扩展定义,在我看来和使用临时表一样(除了你不能在视图中使用临时表),所以我认为这可能是你最好的工作,而不是使用两个CTE,使用两个临时表并将它们连接在一起。

或者,您可以自己仔细检查两个CTE的执行计划,并与两者的执行计划进行比较,找出所有额外成本的来源,不再使用的索引等,但是如果没有你的计划,我甚至无法开始猜测问题是什么。

答案 2 :(得分:0)

在非索引数据上TOP n ORDER BY可能是个问题。

您也可以尝试使用类似

之类的“强制”B列连接的优先级
SELECT     CTE1.C
           ,CTE1.D
           ,CTE2.B
           ,CTE2.C
           ,CTE2.D
           ,CTE2.E
FROM CTE1
  LEFT OUTER JOIN CTE2 ON CTE1.B = CTE2.B
WHERE CTE1.A = CTE2.A
  AND NOT CTE2.B IS NULL