优化一个缓慢的嵌套循环连接

Geo*_*e K 4 performance join sql-server query-performance

这个查询在我的系统上运行了大约 15 秒。查询计划在这里。我相信主要问题就在这里,优化器选择嵌套循环并误认为基数:

在此处输入图片说明

表 _Reference65215 和 _InfoRg100966 分别有 454 和 3332 行。如果我在这里强制 SQL Server 使用哈希匹配,则执行时间会下降到 2 秒。

我不能在这里使用任何类型的提示,因为代码来自平台。该平台使用自己的语言,然后将其解释为 T-SQL。我可以更改查询。统计数据是最新的。Maxdop 1 是平台供应商的一般建议

选择嵌套循环的原因可能是什么以及如何解决它?也许还有其他值得关注的地方?

  EXEC sp_executesql 
         N'SELECT TOP 45 T1._IDRRef, 
                  T1._Marked, 
                  T1._Number, 
                  T1._Date_Time, 
                  T1._Posted, 
                  T1._Fld74271RRef,
                  CASE
                    WHEN T1._Fld74272RRef=@P1
                    THEN T1._Fld74307RRef
                    ELSE T1._Fld74272RRef
                  END, 
                  T1._Fld74273, 
                  T1._Fld74274RRef, 
                  T1._Fld74281,
                  CASE
                    WHEN T1._Fld74272RRef=@P2
                    THEN 0x000100DD
                    WHEN T1._Fld74272RRef=@P3
                         OR T1._Fld74272RRef=@P4
                         OR T1._Fld74272RRef=@P5
                    THEN 0x0000FF7E
                    ELSE 0x0000FF3E
                  END,
                  CASE
                    WHEN T1._Fld74272RRef=@P6
                    THEN T1._Fld74284RRef
                    WHEN T1._Fld74272RRef=@P7
                         OR T1._Fld74272RRef=@P8
                         OR T1._Fld74272RRef=@P9
                    THEN T1._Fld74285RRef
                    ELSE T1._Fld74282RRef
                  END, 
                  T1._Fld74289RRef, 
                  T1._Fld74303,
                  CASE
                    WHEN T1._Fld74333RRef IN( SELECT TOP 1 T12._IDRRef
                                              AS Q_001_F_000RRef
                                              FROM dbo._Reference65527 T12
                                              WHERE T12._Fld67386=@P10
                                              ORDER BY T12._Fld124008 )
                    THEN @P11
                    WHEN T1._Fld74333RRef IN( SELECT TOP 1 T13._IDRRef
                                              AS Q_002_F_000RRef
                                              FROM dbo._Reference65527 T13
                                              WHERE T13._Fld67386=@P12
                                              ORDER BY T13._Fld124008 DESC )
                    THEN @P13
                    ELSE @P14
                  END,
                  CASE
                    WHEN ISNULL( CAST( T2.Fld109931Balance_ AS NUMERIC(27, 2) ), @P15 )<=@P16
                         AND T1._Posted=0x01
                         AND T1._Fld74271RRef<>@P17
                    THEN 0x01
                    ELSE 0x00
                  END,
                  CASE
                    WHEN T11._Fld101929 IS NULL
                    THEN @P18
                    WHEN T11._Fld101929=0x01
                    THEN @P19
                    ELSE @P20
                  END
    FROM dbo._Document65911 T1
    LEFT OUTER JOIN
    (
      SELECT T3._Fld109927_TYPE
      AS Fld109927_TYPE, 
             T3._Fld109927_RTRef
      AS Fld109927_RTRef, 
             T3._Fld109927_RRRef
      AS Fld109927_RRRef, 
             CAST( SUM( T3._Fld109931 ) AS NUMERIC(27, 2) )
      AS Fld109931Balance_
      FROM dbo._AccumRgT109935 T3
      WHERE T3._Fld67386=@P21
            AND EXISTS
      (
        SELECT 0x01
        AS Q_001_F_000_
        FROM dbo._Reference65291 T4
        INNER JOIN
        dbo._Reference65215 T5
        ON T4._Fld118298=@P22
           AND EXISTS
        (
          SELECT 0x01
          AS Q_004_F_000_
          FROM dbo._InfoRg107349 T6
          WHERE T6._Fld67386=@P23
                AND T6._Fld107350RRef=T4._IDRRef
                    AND T6._Fld107351RRef=T5._IDRRef
        )
           AND T5._IDRRef IN
        (
          SELECT T7._Reference65215_IDRRef
          AS Q_005_F_000RRef
          FROM dbo._Reference65215_VT116326 T7
          INNER JOIN
          dbo._InfoRg106545 T8
          ON T8._Fld106547_TYPE=0x08
             AND T8._Fld106547_RTRef=0x0000FFD5
             AND T8._Fld106547_RRRef=@P24
             AND T8._Fld106546_TYPE=T7._Fld116328_TYPE
                 AND T8._Fld106546_RTRef=T7._Fld116328_RTRef
                 AND T8._Fld106546_RRRef=T7._Fld116328_RRRef
          WHERE T7._Fld67386=@P25
                AND T8._Fld67386=@P26
        )
        WHERE T5._Fld67386=@P27
              AND 0x08<>0x01
                  AND CASE
                        WHEN EXISTS
        (
          SELECT 0x01
          AS Q_002_F_000_
          FROM dbo._InfoRg100966 T9
          WHERE T9._Fld67386=@P28
                AND T9._Fld100967RRef=T5._IDRRef
                    AND T9._Fld100968_TYPE=0x08
                        AND T9._Fld100968_RTRef=0x0000FF7E
                        AND T9._Fld100968_RRRef=T3._Fld109930RRef
        )
                        THEN 0x01
                        ELSE 0x00
                      END=CASE
                            WHEN EXISTS
        (
          SELECT 0x01
          AS Q_003_F_000_
          FROM dbo._InfoRg100973 T10
          WHERE T10._Fld67386=@P29
                AND T10._Fld100974RRef=T5._IDRRef
                    AND T10._Fld100975_TYPE=0x08
                    AND T10._Fld100975_RTRef=0x0000FF7E
                    AND T10._Fld100976=0x00
        )
                            THEN 0x01
                            ELSE 0x00
                          END
      )
        AND T3._Period=@P30
            AND T3._Fld109931<>@P31
            AND T3._Fld109931<>@P32
      GROUP BY T3._Fld109927_TYPE, 
               T3._Fld109927_RTRef, 
               T3._Fld109927_RRRef
      HAVING CAST( SUM( T3._Fld109931 ) AS NUMERIC(27, 2) )<>0.0
    ) T2
    ON T2.Fld109927_TYPE=0x08
       AND T2.Fld109927_RTRef=0x00010177
       AND T2.Fld109927_RRRef=T1._IDRRef
        LEFT OUTER JOIN
        dbo._InfoRg101927 T11
        ON(0x08=T11._Fld101928_TYPE
           AND 0x00010177=T11._Fld101928_RTRef
           AND T1._IDRRef=T11._Fld101928_RRRef)
          AND T11._Fld67386=@P33
    WHERE T1._Fld67386=@P34
          AND EXISTS
    (
      SELECT 1
      FROM
      (
        SELECT 1
        AS SDBL_DUMMY
      ) SDBL_DUAL
      LEFT OUTER JOIN
      dbo._Document65911_VT74348 T14
      ON T1._IDRRef=T14._Document65911_IDRRef
         AND T14._Fld67386=T1._Fld67386
          LEFT OUTER JOIN
          dbo._Reference65239 T15
          ON T1._Fld74344_TYPE=0x08
             AND T1._Fld74344_RTRef=0x0000FED7
             AND T1._Fld74344_RRRef=T15._IDRRef
             AND T15._Fld67386=@P35
              LEFT OUTER JOIN
              dbo._Reference65757 T16
              ON T1._Fld74284RRef=T16._IDRRef
                 AND T16._Fld67386=@P36
      WHERE EXISTS
      (
        SELECT 0x01
        AS Q_001_F_000_
        FROM dbo._Reference65291 T17
        INNER JOIN
        dbo._Reference65215 T18
        ON T17._Fld118298=@P37
           AND EXISTS
        (
          SELECT 0x01
          AS Q_015_F_000_
          FROM dbo._InfoRg107349 T19
          WHERE T19._Fld67386=@P38
                AND T19._Fld107350RRef=T17._IDRRef
                    AND T19._Fld107351RRef=T18._IDRRef
        )
           AND T18._IDRRef IN
        (
          SELECT T20._Reference65215_IDRRef
          AS Q_016_F_000RRef
          FROM dbo._Reference65215_VT116326 T20
          INNER JOIN
          dbo._InfoRg106545 T21
          ON T21._Fld106547_TYPE=0x08
             AND T21._Fld106547_RTRef=0x0000FFD5
             AND T21._Fld106547_RRRef=@P39
             AND T21._Fld106546_TYPE=T20._Fld116328_TYPE
                 AND T21._Fld106546_RTRef=T20._Fld116328_RTRef
                 AND T21._Fld106546_RRRef=T20._Fld116328_RRRef
          WHERE T20._Fld67386=@P40
                AND T21._Fld67386=@P41
        )
            LEFT OUTER JOIN
            dbo._Reference65493 T22
            ON @P42=T22._IDRRef
               AND T22._Fld67386=@P43
        WHERE T18._Fld67386=@P44
              AND 0x08<>0x01
                  AND CASE
                        WHEN EXISTS
        (
          SELECT 0x01
          AS Q_002_F_000_
          FROM dbo._InfoRg100966 T23
          WHERE T23._Fld67386=@P45
                AND T23._Fld100967RRef=T18._IDRRef
                    AND T23._Fld100968_TYPE=0x08
                        AND T23._Fld100968_RTRef=0x0000FF7E
                        AND T23._Fld100968_RRRef=T1._Fld74270RRef
        )
                        THEN 0x01
                        ELSE 0x00
                      END=CASE
                            WHEN EXISTS
        (
          SELECT 0x01
          AS Q_003_F_000_
          FROM dbo._InfoRg100973 T24
          WHERE T24._Fld67386=@P46
                AND T24._Fld100974RRef=T18._IDRRef
                    AND T24._Fld100975_TYPE=0x08
                    AND T24._Fld100975_RTRef=0x0000FF7E
                    AND T24._Fld100976=0x00
        )
                            THEN 0x01
                            ELSE 0x00
                          END
                  AND 0x08<>0x01
                  AND NOT(NOT EXISTS
        (
          SELECT 0x01
          AS Q_004_F_000_
          FROM dbo._InfoRg99652 T25
          WHERE T25._Fld67386=@P47
                AND T25._Fld99653_TYPE=0x08
                    AND T25._Fld99653_RTRef=0x0000FFD5
                    AND T25._Fld99653_RRRef=T1._Fld74289RRef
                    AND T25._Fld99654_TYPE=0x08
                        AND T25._Fld99654_RTRef=0x0000FFD5
                        AND T25._Fld99654_RRRef=@P48
        )
                          AND NOT CASE
                                    WHEN EXISTS
        (
          SELECT top 356165165156165165 0x01
          AS Q_005_F_000_
          FROM dbo._InfoRg100966 T26
          INNER  JOIN                ---------------------------------------------------------------------------
          dbo._InfoRg99652 T27
          ON T26._Fld100967RRef=T18._IDRRef

             AND T26._Fld100968_TYPE=T27._Fld99654_TYPE
                 AND T26._Fld100968_RTRef=T27._Fld99654_RTRef
                 AND T26._Fld100968_RRRef=T27._Fld99654_RRRef

             AND T27._Fld99653_TYPE=0x08
                 AND T27._Fld99653_RTRef=0x0000FFD5
                 AND T27._Fld99653_RRRef=T1._Fld74289RRef
          WHERE T26._Fld67386=@P49
                AND T27._Fld67386=@P50
        )
                                    THEN 0x01
                                    ELSE 0x00
                                  END=CASE
                                        WHEN EXISTS
        (
          SELECT 0x01
          AS Q_006_F_000_
          FROM dbo._InfoRg100973 T28
          WHERE T28._Fld67386=@P51
                AND T28._Fld100974RRef=T18._IDRRef
                    AND T28._Fld100975_TYPE=0x08
                    AND T28._Fld100975_RTRef=0x0000FFD5
                    AND T28._Fld100976=0x00
        )
                                        THEN 0x01
                                        ELSE 0x00
                                      END)
                                  AND 0x08<>0x01
                                  AND CASE
                                        WHEN EXISTS
        (
          SELECT 0x01
          AS Q_007_F_000_
          FROM dbo._InfoRg100966 T29
          WHERE T29._Fld67386=@P52
                AND T29._Fld100967RRef=T18._IDRRef
                    AND T29._Fld100968_TYPE=0x08
                        AND T29._Fld100968_RTRef=0x000106FA
                        AND T29._Fld100968_RRRef=T1._Fld74272RRef
        )
                                        THEN 0x01
                                        ELSE 0x00
                                      END=CASE
                                            WHEN EXISTS
        (
          SELECT 0x01
          AS Q_008_F_000_
          FROM dbo._InfoRg100973 T30
          WHERE T30._Fld67386=@P53
                AND T30._Fld100974RRef=T18._IDRRef
                    AND T30._Fld100975_TYPE=0x08
                    AND T30._Fld100975_RTRef=0x000106FA
                    AND T30._Fld100976=0x00
        )
                                            THEN 0x01
                                            ELSE 0x00
                                          END
                                  AND CASE
                                        WHEN T1._Fld74272RRef=@P54
                                             OR T1._Fld74272RRef=@P55
                                        THEN CASE
                                               WHEN CASE
                                                      WHEN T14._Fld74350RRef IS NULL
                                                      THEN 0x01
                                                      ELSE CASE
                                                             WHEN T14._Fld74350RRef IS NOT NULL
                                                             THEN 0x08
                                                           END
                                                    END<>0x01
                                               THEN 0x01
                                               ELSE 0x00
                                             END
                                        WHEN T1._Fld74272RRef=@P56
                                        THEN CASE
                                               WHEN 0x08<>0x01
                                               THEN 0x01
                                               ELSE 0x00
                                             END
                                        ELSE 0x01
                                      END=0x01
                                  AND CASE
                                        WHEN T1._Fld74272RRef=@P57
                                             OR T1._Fld74272RRef=@P58
                                        THEN CASE
                                               WHEN 0x08<>0x01
                                                    AND CASE
                                                          WHEN EXISTS
        (
          SELECT 0x01
          AS Q_011_F_000_
          FROM dbo._InfoRg100966 T31
          WHERE T31._Fld67386=@P59
                AND T31._Fld100967RRef=T18._IDRRef
                    AND T31._Fld100968_TYPE=0x08
                        AND T31._Fld100968_RTRef=0x0001007C
                        AND T31._Fld100968_RRRef=ISNULL( T15._Fld116737RRef, @P60 )
        )
                                                          THEN 0x01
                                                          ELSE 0x00
                                                        END=CASE
                                                              WHEN EXISTS
        (
          SELECT 0x01
          AS Q_012_F_000_
          FROM dbo._InfoRg100973 T32
          WHERE T32._Fld67386=@P61
                AND T32._Fld100974RRef=T18._IDRRef
                    AND T32._Fld100975_TYPE=0x08
                    AND T32._Fld100975_RTRef=0x0001007C
                    AND T32._Fld100976=0x00
        )
                                                              THEN 0x01
                                                              ELSE 0x00
                                                            END
                                                    AND 0x08<>0x01
                                                        AND CASE
                                                              WHEN EXISTS
        (
          SELECT 0x01
          AS Q_013_F_000_
          FROM dbo._InfoRg100966 T33
          WHERE T33._Fld67386=@P62
                AND T33._Fld100967RRef=T18._IDRRef
                    AND T33._Fld100968_TYPE=0x08
                        AND T33._Fld100968_RTRef=0x00010103
                        AND T33._Fld100968_RRRef=ISNULL( T15._Fld116803RRef, @P63 )
        )
                                                              THEN 0x01
                                                              ELSE 0x00
                                                            END=CASE
                                                                  WHEN EXISTS
        (
          SELECT 0x01
          AS Q_014_F_000_
          FROM dbo._InfoRg100973 T34
          WHERE T34._Fld67386=@P64
                AND T34._Fld100974RRef=T18._IDRRef
                    AND T34._Fld100975_TYPE=0x08
                    AND T34._Fld100975_RTRef=0x00010103
                    AND T34._Fld100976=0x00
        )
                                                                  THEN 0x01
                                                                  ELSE 0x00
                                                                END
                                               THEN 0x01
                                               ELSE 0x00
                                             END
                                        ELSE CASE
                                               WHEN 0x08<>0x01
                                                    AND CASE
                                                          WHEN EXISTS
        (
          SELECT 0x01
          AS Q_009_F_000_
          FROM dbo._InfoRg100966 T35
          WHERE T35._Fld67386=@P65
                AND T35._Fld100967RRef=T18._IDRRef
                    AND T35._Fld100968_TYPE=0x08
                        AND T35._Fld100968_RTRef=0x0001007C
                        AND T35._Fld100968_RRRef=T1._Fld74288RRef
        )
                                                          THEN 0x01
                                                          ELSE 0x00
                                                        END=CASE
                                                              WHEN EXISTS
        (
          SELECT 0x01
          AS Q_010_F_000_
          FROM dbo._InfoRg100973 T36
          WHERE T36._Fld67386=@P66
                AND T36._Fld100974RRef=T18._IDRRef
                    AND T36._Fld100975_TYPE=0x08
                    AND T36._Fld100975_RTRef=0x0001007C
                    AND T36._Fld100976=0x00
        )
                                                              THEN 0x01
                                                              ELSE 0x00
                                                            END
                                               THEN 0x01
                                               ELSE 0x00
                                             END
                                      END=0x01
                                  AND (0x08<>0x01
                                       OR T1._Fld74284RRef=T22._Fld123105RRef)
      )
    )
    ORDER BY T1._Date_Time DESC, 
             T1._IDRRef DESC', 
         N'@P1 varbinary(16),@P2 varbinary(16),@P3 varbinary(16),@P4 varbinary(16),@P5 varbinary(16),@P6 varbinary(16),@P7 varbinary(16),@P8 varbinary(16),@P9 varbinary(16),@P10 numeric(10),@P11 numeric(10),@P12 numeric(10),@P13 numeric(10),@P14 numeric(10),@P15 numeric(10),@P16 numeric(10),@P17 varbinary(16),@P18 numeric(10),@P19 numeric(10),@P20 numeric(10),@P21 numeric(10),@P22 nvarchar(4000),@P23 numeric(10),@P24 varbinary(16),@P25 numeric(10),@P26 numeric(10),@P27 numeric(10),@P28 numeric(10),@P29 numeric(10),@P30 datetime2(3),@P31 numeric(10),@P32 numeric(10),@P33 numeric(10),@P34 numeric(10),@P35 numeric(10),@P36 numeric(10),@P37 nvarchar(4000),@P38 numeric(10),@P39 varbinary(16),@P40 numeric(10),@P41 numeric(10),@P42 varbinary(16),@P43 numeric(10),@P44 numeric(10),@P45 numeric(10),@P46 numeric(10),@P47 numeric(10),@P48 varbinary(16),@P49 numeric(10),@P50 numeric(10),@P51 numeric(10),@P52 numeric(10),@P

Ran*_*gen 8

我会添加OPTION(USE HINT 'DISABLE_OPTIMIZER_ROWGOAL')到查询以禁用行目标

发现行目标使用的最简单方法是使用TOP()运算符,用于在您的情况下尽快返回 45 行。

循环连接的使用在这里更常见,因为它估计较小的结果集并使用非阻塞运算符。

在大多数情况下,行目标会有所帮助,但是对于复杂的大型查询,我看到它们过去很麻烦。

通过禁用这些行目标,您应该为整个查询获得更优化的计划。

您还可以通过添加OPTION(QUERYTRACEON 4138)到查询的末尾来禁用它。


我试图删除 TOP 45 部分 - 现在它的工作时间不到 1 秒。我在哪里可以更深入地了解行目标?

TOP(45)在这种情况下,删除已删除的行目标并提高性能。

Paul White 有一些关于行目标的很棒的博客文章。第 1 部分和第 2 部分可以在此处此处找到。优化器内部也有:深度行目标

如果您无法更改查询,则可以为特定查询添加计划指南以禁用行目标。


另一种在不添加提示的情况下删除行目标的方法

根据声明:

要记住的第二个一般点是,仅当目标低于常规估计值时才设置行目标

再次来自:在执行计划中设置和识别行目标

您可以TOP()使用如此高的数字指定一个无意义的数字,以至于不会设置行目标,然后将您的实际内容包裹TOP()起来。

例如:

SELECT TOP(5) *
FROM(
SELECT TOP(99999999999) *
FROM dbo.table
ORDER BY column ASC
) AS  A;
Run Code Online (Sandbox Code Playgroud)

更多例子:

数据库<>小提琴

简而言之,TOP(5)rowgoal 被top(99999999999)内部查询中更高的值覆盖,导致在我的示例中只剩下 2 个实际的行目标,一个在TOP(99999999)运算符上:

SELECT TOP(5) *
FROM(
SELECT TOP(99999999999) *
FROM dbo.table
ORDER BY column ASC
) AS  A;
Run Code Online (Sandbox Code Playgroud)

嵌套循环运算符上的一个紧跟在第二个TOP()完全匹配EstimateRows

PhysicalOp="Top" LogicalOp="Top" EstimateRows="5" EstimateRowsWithoutRowGoal="9.4199"
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

对于像您这样的行目标问题的真实示例,由于行目标,我能够使用非阻塞运算符(例如 NL 连接)更改计划形状TOP(5)

屏幕截图中未显示实际性能问题

在此处输入图片说明

使用TOP(999999999)方法散列匹配运算符。
在应用行目标禁用提示时,此计划形状与查询匹配。

在此处输入图片说明

我仍然希望添加查询提示而不是编写两个TOP()s。但是,如果您不被允许或不能,这可能会有所帮助。