简单的更改会导致 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
但是当我添加一个标志变量以使我能够 "turn on/off" 内部查询中的过滤器,并将标志设置为零时,查询似乎无限期地执行(我离开它 运行ning 大约 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 在评论中的要求,这是第一个查询的执行计划:
这是第二个查询的执行计划:
(我无法 post 第三个查询的执行计划,因为它永远不会完成 - 除非有另一种方法可以在 SSMS 中获取它?)
试试这个:
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
组合不是我写过的最容易理解的东西,但它应该在功能上等同于你所拥有的,只要连接条件匹配加入了 table.
Why? What am I doing wrong?
您正在尝试编译 需要根据变量满足多个不同条件的查询。优化器必须提出 one 在 both 情况下有效的计划。
尽量避免这种情况,就像避免瘟疫一样。只需发出两个查询,一个针对一个条件,一个针对另一个条件,这样优化器就可以自由地分别优化每个查询,并编译针对每种情况的最佳执行计划。
对该主题的长时间讨论,以及替代方案和利弊:Dynamic Search Conditions in T‑SQL
Joel +1 的好回答
OR 很难优化
回到第二个
优化器难以优化的地方
考虑在
中加入所有那些
这仍然有一个 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
我在我的 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
但是当我添加一个标志变量以使我能够 "turn on/off" 内部查询中的过滤器,并将标志设置为零时,查询似乎无限期地执行(我离开它 运行ning 大约 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 在评论中的要求,这是第一个查询的执行计划:
这是第二个查询的执行计划:
(我无法 post 第三个查询的执行计划,因为它永远不会完成 - 除非有另一种方法可以在 SSMS 中获取它?)
试试这个:
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
组合不是我写过的最容易理解的东西,但它应该在功能上等同于你所拥有的,只要连接条件匹配加入了 table.
Why? What am I doing wrong?
您正在尝试编译 需要根据变量满足多个不同条件的查询。优化器必须提出 one 在 both 情况下有效的计划。
尽量避免这种情况,就像避免瘟疫一样。只需发出两个查询,一个针对一个条件,一个针对另一个条件,这样优化器就可以自由地分别优化每个查询,并编译针对每种情况的最佳执行计划。
对该主题的长时间讨论,以及替代方案和利弊:Dynamic Search Conditions in T‑SQL
Joel +1 的好回答
OR 很难优化
回到第二个
优化器难以优化的地方
考虑在
中加入所有那些
这仍然有一个 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