繁体   English   中英

简单的更改会导致SQL查询执行时间大幅增加

[英]Simple change causes SQL query execution time to dramatically increase

我在我的Microsoft SQL Server(2012 Express)数据库上运行以下SQL查询,它工作正常,在不到一秒的时间内执行:

SELECT
  StringValue, COUNT(StringValue)
FROM Attributes
WHERE
  Name = 'Windows OS Version'
  AND StringValue IS NOT NULL
  AND ProductAssociation IN (
    SELECT ID
    FROM ProductAssociations
    WHERE ProductCode = 'MyProductCode'
  )
GROUP BY StringValue

我在内部查询中添加了一个过滤器,它继续正常工作,返回的结果略少(如预期的那样),并且在不到一秒的时间内执行。

SELECT
  StringValue, COUNT(StringValue)
FROM Attributes
WHERE
  Name = 'Windows OS Version'
  AND StringValue IS NOT NULL
  AND ProductAssociation IN (
    SELECT ID
    FROM ProductAssociations
    WHERE ProductCode = 'MyProductCode'
    AND ID IN (
      SELECT A2.ProductAssociation
      FROM Attributes A2
      WHERE A2.Name = 'Is test' AND A2.BooleanValue = 0
    )
  )
GROUP BY StringValue

但是当我添加一个标志变量以使我能够“打开/关闭”内部查询中的过滤器,并将标志设置为零时, 查询似乎无限期地执行 (我让它运行大约5分钟,然后强制取消) :

DECLARE @IsTestsIncluded bit
SET @IsTestsIncluded = 0

SELECT
  StringValue, COUNT(StringValue)
FROM Attributes
WHERE
  Name = 'Windows OS Version'
  AND StringValue IS NOT NULL
  AND ProductAssociation IN (
    SELECT ID
    FROM ProductAssociations
    WHERE ProductCode = 'MyProductCode'
    AND (
      @IsTestsIncluded = 1
      OR
      ID IN (
        SELECT A2.ProductAssociation
        FROM Attributes A2
        WHERE A2.Name = 'Is test' AND A2.BooleanValue = 0
      )
    )
  )
GROUP BY StringValue

为什么? 我究竟做错了什么? 我发誓我过去使用过这种模式没有问题。

(当我在上面的最终查询中设置@IsTestsIncluded = 1时,跳过过滤器并且执行时间正常 - 延迟仅在@IsTestsIncluded = 0时发生)


编辑

根据Joel在评论中的请求,这是第一个查询的执行计划:

第一次查询的执行计划

这是第二个查询的执行计划:

在此输入图像描述

(我不能发布第三个查询的执行计划,因为它永远不会完成 - 除非有其他方法可以在SSMS中获取它吗?)

为什么? 我究竟做错了什么?

您正在尝试根据变量编译需要满足多个不同条件的查询。 优化器必须提出一个适用于这两种情况的计划。

试着像瘟疫一样避免这种情况。 只需发出两个查询,一个用于另一个条件,因此优化器可以自由地分别优化每个查询并编译对每种情况都是最佳的执行计划。

关于该主题的长篇讨论,以及替代方案和优点和缺点: T-SQL中的动态搜索条件

尝试这个:

SELECT
  a.StringValue, COUNT(a.StringValue)
FROM Attributes a
INNER JOIN ProductAssociations p ON a.ProductAssociation = p.ID
    AND p.ProductCode = 'MyProductCode'
LEFT JOIN Attributes a2 ON a2.ProductAssociation = p.ID
    AND a2.Name = 'Is Test' AND a2.BooleanValue = 0       
WHERE
  Name = 'Windows OS Version'
  AND StringValue IS NOT NULL
  AND COALESCE(a2.ProductAssociation, NULLIF(@IsTestsIncluded, 1)) IS NOT NULL
GROUP BY a.StringValue

coalesce/nullif组合不是我写过的最容易coalesce/nullif东西,但只要连接条件匹配连接表上的0或1记录,它就应该在功能上等同于你拥有的东西。

乔尔+1给出了很好的答案

或者很难优化

回到第二个
优化者难以优化的地方
考虑加入所有那里的人
这仍然具有可能导致错误查询计划的OR,但它为优化器提供了最小化OR的机会

SELECT A1.StringValue, COUNT(A1.StringValue)
 FROM Attributes A1
 JOIN ProductAssociations PA
   ON PA.ID = A1.ProductAssociation
  AND A1.Name = 'Windows OS Version'
  AND A1.StringValue IS NOT NULL
  AND PA.ProductCode = 'MyProductCode'
 JOIN Attributes A2 
   ON A2.ProductAssociation = A1.ProductAssociation 
  AND (     @IsTestsIncluded = 1
        OR (A2.Name = 'Is test' AND A2.BooleanValue = 0)
      )
GROUP BY A1.StringValue  

如果你重构@IsTestsIncluded,你可以这样做

SELECT A1.StringValue, COUNT(A1.StringValue)
 FROM Attributes A1
 JOIN ProductAssociations PA
   ON PA.ID = A1.ProductAssociation
  AND A1.Name = 'Windows OS Version'
  AND A1.StringValue IS NOT NULL
  AND PA.ProductCode = 'MyProductCode'
 LEFT JOIN Attributes A2 
   ON A2.ProductAssociation = A1.ProductAssociation 
  AND A2.Name = 'Is test' 
  AND A2.BooleanValue = 0
WHERE ISNULL(@IsTestsIncluded, A2.ProductAssociation) is NOT NULL
GROUP BY A1.StringValue 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM