对 SQL SERVER 查询的微小更改会导致执行时间极慢

Posted

技术标签:

【中文标题】对 SQL SERVER 查询的微小更改会导致执行时间极慢【英文标题】:Minor change to SQL SERVER query causes extremely slow execution time 【发布时间】:2015-05-29 23:59:00 【问题描述】:

我不明白这 2 个查询在功能上有何不同会使它们如此不同。首先是我的初始查询:

SELECT * FROM XSales_Code SC
    WHERE SC.Status = 1
        AND SC.SCode NOT IN
            (
            SELECT DISTINCT SCode FROM XTransactions_01
            WHERE Last_Mdt > '2012-01-01'
                AND SCode IS NOT NULL
            )
        AND SC.Last_Mdt < '2014-01-01'
ORDER BY Last_Mdt desc

这需要 13 分 6 秒来执行。因为我习惯了像这样的简单查询,而不是几分钟,所以我玩弄了这个查询,至少在我看来,它是等效的:

SELECT DISTINCT SCode INTO #TEMP1 FROM XTransactions_01
WHERE Last_Mdt > '2012-01-01'
    AND SCode IS NOT NULL

SELECT * FROM XSales_Code SC
    WHERE SC.Status = 1
        AND SC.SCode NOT IN
            (
            SELECT Scode FROM #TEMP1
            )
        AND SC.Last_Mdt < '2014-01-01'
ORDER BY Last_Mdt desc

DROP TABLE #TEMP1

不同之处在于此查询需要 2 秒才能执行,而上面的 13 分钟。这是怎么回事?

【问题讨论】:

我没有。我认为这是罪魁祸首。这是长时间表:...表'XTransactions_01'。 ... 逻辑读取 300636371... 短时间表:...XTransactions_01'.... 逻辑读取 43380... 这次我确实抓住了执行计划但是我不知道如何阅读它们.. 如果您在查询计划中看到索引扫描而不是索引查找,这是最容易查找的内容。扫描意味着它必须读取索引中的所有数据,因为具有条件的列不是索引列。此外,如果您在统计数据中看到物理读取,那么物理读取是“真正的”杀手,因为这意味着它必须实际进入磁盘而不是能够从内存中获取它。 您也可以尝试使用不存在来执行此操作,如果效果更好:“AND 不存在(从 XTransactions_01 X 中选择 1,其中 X.SCode = SC.SCode 和 Last_Mdt > '2012-01-01 ')”。此外,查看实际计划中的估计行数和实际行数可能有助于理解查询计划出错的原因。 【参考方案1】:

在这两种情况下,您都在使用“相关子查询”,它针对XSales_Code 中通过Status = 1 AND Last_Mdt &lt; '2014-01-01' 条件的每一行执行。

可以这样想:XSales_CodeStatus = 1 AND Last_Mdt &lt; '2014-01-01' 过滤,然后 SQL Server 扫描此中间结果的每一行,并针对每一行执行您的 SELECT DISTINCT SCode FROM XTransactions_01... 查询以查看是否应包含该行.

您的第二个查询执行相关子查询的次数相同,但速度更快,因为它是针对较小的表执行的。

通常,执行NOT IN 查询的最快方法是左连接到“不在”子查询中,然后省略左连接列为空的任何行。这摆脱了相关的子查询。

SELECT * FROM XSales_Code SC
LEFT JOIN (
    SELECT DISTINCT SCode FROM XTransactions_01
    WHERE Last_Mdt > '2012-01-01'
        AND SCode IS NOT NULL
) whatevs ON SC.SCode = whatevs.SCode
WHERE SC.Status = 1
  AND SC.Last_Mdt < '2014-01-01'
  AND whatevs.SCode IS NULL
ORDER BY Last_Mdt desc

这很难解释,但是尝试在没有倒数第二行 (AND whatevs.SCode IS NULL) 的情况下运行上面的查询,您将看到whatevs.SCODE 在条件为“IN”且为空时如何具有值当条件为“NOT IN”时。

最后,我想强调的是,相关子查询本质上并不是邪恶的。通常,它们在 IN 条件和许多其他用例中工作得很好,但对于 NOT IN 条件,它们往往很慢。

【讨论】:

以上是关于对 SQL SERVER 查询的微小更改会导致执行时间极慢的主要内容,如果未能解决你的问题,请参考以下文章

sqlserver分页去重失效

SQL Server - 条件语句的查询执行计划

SQL Server中TOP子句可能导致的问题以及解决办法

SQL Server 2005 缓存

CTE 中的 SQL Server 视图导致性能不佳

SQL Server 子查询会导致性能损失吗?