多个INNER JOIN太慢了SQL SERVER

7
我有一个性能问题。
我创建了一个从文件接收数据的表,使用BULK INSERT。然后我使用多个INNER JOIN进行SELECT,将正确的数据插入到另一个表中。
当我运行这个SELECT时,它需要太长时间(超过一小时),然后我停止了它。我的解决方案是将这个查询分成3个,创建@temp表。令我惊讶的是,这只需要3分钟。我试图理解的是,为什么将我的查询分成3个比一个选择语句更快。以下是我的查询:
SELECT t1.ReturnINT, t1.ReturnBIT, t2.ReturnINT, t3.ReturnINT, t5.ReturnINT, t1.ReturnDateTime
FROM t1
INNER JOIN t2
    ON t2.my_column_varchar = t1.my_column_varchar
INNER JOIN t3
    ON t3.my_column_number = t1.my_column_number AND t2.my_column_ID = t3.my_column_ID
INNER JOIN t4
    ON t4.my_column_varchar = t1.my_column_varchar
INNER JOIN t5
    ON t5.my_column_int = t1.my_column_int AND t5.my_column_int = t4.my_column_int AND t2.my_column_int = t5.my_column_int
INNER JOIN t6
    ON t6.my_column_int = t5.my_column_int AND t6.my_column_int = t2.my_column_int
INNER JOIN t7
    ON t7.my_column_int = t6.my_column_int
INNER JOIN t8
    ON t8.my_column_int = t3.my_column_int AND t8.my_column_datetime = t1.my_column_datetime
INNER JOIN t9
    ON t9.my_column_int = t3.my_column_int AND t8.my_column_datetime BETWEEN t9.my_column_datetime1 AND t9.datetime1 + t9.my_column_datetime2
INNER JOIN t10
    ON t10.my_column_int = t9.my_column_int AND t10.my_column_int = t6.my_column_int
INNER JOIN t11
    ON t11.my_column_int = t9.my_column_int AND t8.my_column_datetime = t11.my_column_datetime

----编辑----

没有where子句,我的查询就像我在这里放的一样。

这是我的错误查询语句,我忘记把它们放在这里了。它需要3分钟才能运行。

DECLARE @temp TABLE (
    <Some_columns>
)
INSERT INTO @temp
    SELECT <My_Linked_Columns>
    FROM t1
    INNER JOIN t2
        ON t2.my_column_varchar = t1.my_column_varchar
    INNER JOIN t3
        ON t3.my_column_number = t1.my_column_number AND t2.my_column_ID = t3.my_column_ID
    INNER JOIN t4
        ON t4.my_column_varchar = t1.my_column_varchar
    INNER JOIN t5
        ON t5.my_column_int = t1.my_column_int AND t5.my_column_int = t4.my_column_int AND t2.my_column_int = t5.my_column_int


DECLARE @temp2 TABLE(
    <Some_Columns>
)
INSERT INTO @temp2
    SELECT <More_Linked_Columns>
    FROM @temp as temp
    INNER JOIN t6
        ON t6.my_column_int = temp.my_column_int AND t6.my_column_int = temp.my_column_int
    INNER JOIN t7
        ON t7.my_column_int = t6.my_column_int
    INNER JOIN t8
        ON t8.my_column_int = temp.my_column_int AND t8.my_column_datetime = temp.my_column_datetime


DECLARE @temp3 TABLE(
    <Some_Columns>
)
INSERT INTO @temp3
    SELECT <More_Linked_Columns>
    FROM @temp2 AS temp2
    INNER JOIN t9
        ON t9.my_column_int = temp2.my_column_int AND temp2.my_column_datetime BETWEEN t9.my_column_datetime1 AND t9.datetime1 + t9.my_column_datetime2
    INNER JOIN t10
        ON t10.my_column_int = t9.my_column_int AND t10.my_column_int = temp2.my_column_int
    INNER JOIN t11
        ON t11.my_column_int = t9.my_column_int AND temp2.my_column_datetime = t11.my_column_datetime


SELECT <All_Final_Columns>
FROM @temp3

----EDITED 3----

在研究更多事情时,我发现了执行计划中的一个问题。我有一个嵌套循环,估计返回1行,但实际上返回了1,204,014行。我猜问题就出在这里,但我还没有找出如何在不将查询分解为3部分的情况下解决此问题(现在我知道为什么分解它会更快啦)。


2
这可能是许多问题中的任何一个。它可能是索引问题,也可能是在where子句中有nonSARGable谓词,还有很多其他可能性。如果没有一些实际细节,任何人都只能猜测是什么导致了这个缓慢的问题。 - Sean Lange
1
它减少了优化器决定连接顺序和连接类型的选择。 - Pரதீப்
尝试使用LEFT OUTER JOIN。我通过将SQL Server 2008 R2中的内部连接转换为左外连接,将执行时间从38秒缩短到1秒。 - Anoopkumar
4个回答

7

最常见的原因:

原因1:当参与INNER JOIN的两个表具有多对多关系,其中一个表有n行,另一个表有m行时,INNER JOIN可能会接近于CROSS JOIN,并能够产生结果集,其行数超过了最大行数(MAX(n,m)),在理论上,可能会有n x m行。

现在想象一下,在INNER JOIN中有许多这样的表。

这将导致结果集变得越来越大,并开始占用分配的内存区域。

这可能是临时表可以帮助您的原因。

原因2:您在连接表格的列上没有建立INDEX

原因3:您的WHERE子句中是否有函数?


2
一般来说,您希望查询优化器以尽可能限制结果集的方式连接表。如果您有一个包含100万行的A表,一个包含100万行的B表和一个包含10行的C表,则应首先从C表与A或B表进行内部连接,这将最多给您提供10条记录(假设1:1匹配),然后再连接到最后一个表。如果您首先连接A和B表,则会连接每个表中的所有100万行,这将需要更长的时间。

通常,查询优化器在选择连接顺序方面是“足够好”的,但在您的情况下可能不是。我见过的最好的强制连接顺序的方法是由Adam Mechanic在此博客文章中演示。它涉及在要从中开始连接的表上使用TOP子句。查询优化器将首先从这些表中获取结果集,并且您可以真正限制总行数并增加查询性能。我尽可能使用此方法。

0

通过正确的索引,原始查询应该能够非常快速地执行(如果您对数据进行分页,则不到一秒钟)。不要使用临时表作为无法想出合理查询的替代方法。


0

可能有几种不同的情况,但从你的描述来看,你已经检查了索引和执行计划。我建议观看Adam Mechanic关于“行目标”和使用顶部语句的视频,这类似于临时表的工作方式。

SELECT *
FROM
  (SELECT top (2000000000) t1.ReturnINT,
          t1.ReturnBIT,
          t2.ReturnINT,
          t3.ReturnINT,
          t5.ReturnINT,
          t1.ReturnDateTime
   FROM t1
   INNER JOIN t2 ON t2.my_column_varchar = t1.my_column_varchar
   INNER JOIN t3 ON t3.my_column_number = t1.my_column_number
       AND t2.my_column_ID = t3.my_column_ID
   INNER JOIN t4 ON t4.my_column_varchar = t1.my_column_varchar
   INNER JOIN t5 ON t5.my_column_int = t1.my_column_int
       AND t5.my_column_int = t4.my_column_int
       AND t2.my_column_int = t5.my_column_int
   INNER JOIN t6 ON t6.my_column_int = t5.my_column_int
       AND t6.my_column_int = t2.my_column_int
   INNER JOIN t7 ON t7.my_column_int = t6.my_column_int
   INNER JOIN t8 ON t8.my_column_int = t3.my_column_int
       AND t8.my_column_datetime = t1.my_column_datetime
   INNER JOIN t9 ON t9.my_column_int = t3.my_column_int
       AND t8.my_column_datetime BETWEEN t9.my_column_datetime1 
       AND t9.datetime1 + t9.my_column_datetime2
   INNER JOIN t10 ON t10.my_column_int = t9.my_column_int
       AND t10.my_column_int = t6.my_column_int
   INNER JOIN t11 ON t11.my_column_int = t9.my_column_int
       AND t8.my_column_datetime = t11.my_column_datetime
)

我曾经遇到过类似的问题,但是在多次连接之后加上了一个where子句,将一个需要10分钟才能完成的查询缩短到了39秒。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接