SQL Server 单独运行时查询速度快,但添加到联接中时查询速度慢

6tr1vspr  于 2022-12-03  发布在  其他
关注(0)|答案(3)|浏览(293)

在SQL Server中,此查询的运行速度非常快,不到一秒:

SELECT T1.id
FROM first AS T1
WHERE T1.id = 21

这个查询也运行得非常快,不到一秒,尽管它有5300万条记录,但只有大约6条ID为21的记录:

SELECT TOP 1 T2.value
FROM second AS T2 WITH(INDEX(IX_second))
WHERE T2.id = 21 
  AND T2.b = 1 
  AND T2.c = 0 
  AND T2.d = 0 
  AND T2.e = 0
ORDER BY T2.id, T2.b, T2.c, T2.d, T2.e, T2.timestamp DESC

但是,在这个查询中,我将内部SELECT中的21替换为T1.id,速度非常非常慢,超过80秒:

SELECT T1.id, T3.value
FROM first AS T1
JOIN second AS T3 ON T3.id IN (SELECT TOP 1 T2.id
                               FROM second AS T2 WITH(INDEX(IX_second))
                               WHERE T2.id = T1.id 
                                 AND T2.b = 1 
                                 AND T2.c = 0 
                                 AND T2.d = 0 
                                 AND T2.e = 0
                               ORDER BY T2.id, T2.b, T2.c, T2.d, T2.e, T2.timestamp DESC)
 WHERE T1.id = 21

为什么这个查询要花这么长的时间,我如何使它更快?
编辑:以下是计划,更改了一些表名和字段名以保护无辜:)brentozar.com/pastetheplan/?id=rJYBSfwws

mrfwxfqh

mrfwxfqh1#

这看起来像是可以使用CROSS APPLY的情况。这允许使用TOP 1ORDER BY,但会避免对second表的双重引用。
试试看:

SELECT T1.id, T3.value
FROM first AS T1
CROSS APPLY (
    SELECT TOP 1 T2.*
    FROM second AS T2 --WITH(INDEX(IX_second))
    WHERE T2.id = T1.id 
    AND T2.b = 1 
    AND T2.c = 0 
    AND T2.d = 0 
    AND T2.e = 0
    ORDER BY T2.id, T2.b, T2.c, T2.d, T2.e, T2.timestamp DESC
) T3
WHERE T1.id = 21

如果IX_secondsecond(id)上的索引,则SQL Server查询优化器可能会选择该索引,而不需要索引提示。
只是为了检查:你确定你指的是T2.id = T1.id而不是T2.first_id = T1.id之类的东西吗?
还有一点要注意:由于T2.id, T2.b, T2.c, T2.d, T2.eCROSS APPLY结果中都是固定的,因此可以将它们从ORDER BY子句中删除。
ADDENDUM:根据您发布的执行计划中的查询,上面的内容相当于:

SELECT *
FROM Event_Item AS ei
CROSS APPLY (
    SELECT TOP 1 eisp1.*
    FROM Event_Item_Spread AS eisp1
        -- WITH(INDEX(IX_Event_Item_Spread__event_item__sportsbook__period__ingame__alt__timestamp_desc))
    WHERE eisp1.event_item_id = ei.id
      AND eisp1.sportsbook_id = 1
      AND eisp1.period = 0
      AND eisp1.in_game = 0
      AND eisp1.alt = 0
    ORDER BY eisp1.timestamp DESC
) AS eisp
WHERE ei.id = 39604314
n3schb8v

n3schb8v2#

这一切都取决于DBMS计算数据的方式。但在这种情况下,53M将被处理(T1 X T3)次。每次都将进行排序、反转、查询,如果索引不是用值(b,c,d,e...)构建的,还可能与源表进行一些连接。
我不明白您的查询逻辑。为什么不使用一个连接...

SELECT T1.id, T3.value
  FROM first AS T1
  JOIN second AS T2 ON T1.id = T2.id 
 WHERE T1.id = 21 
  AND T2.b = 1 AND T2.c = 0 AND T2.d = 0 AND T2.e = 0

这看起来与上述结果相同。

2jcobegt

2jcobegt3#

IN()语句中使用子查询在语法上是可以的,但我不推荐这样做,因为管理表之间的JOIN的速度较慢。
正如其他人所建议的,使用一个 * 临时表 *。在我看来,即使是CTE()也至少是一个更干净的代码版本,更容易理解。您还需要研究您的执行计划。
我还没有测试过这个,但是可以尝试这样做:

SELECT TOP 1 T2.id
    INTO #tblTEMP1
    FROM second AS T2 
    WHERE T2.b = 1 
      AND T2.c = 0 
      AND T2.d = 0 
      AND T2.e = 0
    ORDER BY T2.id, T2.timestamp DESC
..
..
    SELECT T1.id, T3.value
    FROM first AS T1
    INNER JOIN #tblTEMP1 AS t ON t.ID = t1.ID
    INNER JOIN second AS T3 ON T3.id = t.ID
    WHERE T1.id = 21

我敢打赌这会更快,但可能没有你想象的那么快。
同样,您需要研究每个案例的执行计划,确定确切的瓶颈,并在必要的地方放置INDEXes

相关问题