sql 分配给变量时的执行延迟
sql execution latency when assign to a variable
以下查询将在大约 22 秒后 运行:
DECLARE @i INT, @x INT
SET @i = 156567
SELECT
TOP 1
@x = AncestorId
FROM
dbo.tvw_AllProjectStructureParents_ChildView a
WHERE
ProjectStructureId = @i AND
a.NodeTypeCode = 42 AND
a.AncestorTypeDiffLevel = 1
OPTION (RECOMPILE)
问题出在 变量赋值 (确实是这一行:@x = AncestorId
)。删除分配时,它会加速到大约 15 毫秒!
我通过将结果插入临时 table 解决了这个问题,但我认为这是一种糟糕的方法。
任何人都可以帮我解决问题的根源是什么?!
P.S.
错误的执行计划(22s):https://www.brentozar.com/pastetheplan/?id=Sy6a4c9bW
好的执行计划(20ms):https://www.brentozar.com/pastetheplan/?id=Byg8Hc5ZZ
差异可能来自SELECT TOP 1.
当你只有字段时,SQL服务器将只取第一行。当你有变量赋值时 SQL 服务器正在获取所有结果但只使用最前面的结果。
我检查了不同的查询,并不总是这样,但可能在这里 SQL 服务器优化失败,因为 views/tables。
您可以尝试以下解决方法:
DECLARE @i INT, @x INT
SET @i = 156567
SET @x = (SELECT
TOP 1
AncestorId
FROM
dbo.tvw_AllProjectStructureParents_ChildView a
WHERE
ProjectStructureId = @i AND
a.NodeTypeCode = 42 AND
a.AncestorTypeDiffLevel = 1)
当你使用OPTION (RECOMPILE)
SQL服务器时一般可以执行parameter embedding optimisation.
它正在编译的计划是一次性使用的,因此它可以嗅探所有变量和参数的值并将它们视为常量。
下面是一个简单的例子,展示了参数嵌入优化的作用以及分配给变量的效果(未估计实际执行计划)。
DECLARE @A INT = 1,
@B INT = 2,
@C INT;
SELECT TOP (1) number FROM master..spt_values WHERE @A > @B;
SELECT TOP (1) number FROM master..spt_values WHERE @A > @B OPTION (RECOMPILE);
SELECT TOP (1) @C = number FROM master..spt_values WHERE @A > @B OPTION (RECOMPILE);
这方面的计划如下
请注意,中间那个甚至根本没有触及 table,因为 SQL 服务器可以在编译时推断出 @A > @B
不是 true
。但是计划 3 又回到计划中包含 table,因为变量赋值显然阻止了计划 2 中显示的 OPTION (RECOMPILE)
的影响。
(顺便说一句,第三个计划实际上并不是第一个计划的 4-5 倍。分配给变量似乎也抑制了通常的行目标逻辑,在该逻辑中,索引扫描的成本将缩减为反映 TOP 1
)
在你的好计划中,156567
的 @i
值被推入递归 CTE 锚腿中的查找,它返回 0 行,因此递归部分必须不做工作。
在你糟糕的计划中,递归 CTE 完全实现了递归子树的 627,393 次执行,最后将谓词应用于结果 627,393 行(丢弃所有行)
我不确定为什么 SQL 服务器无法下推带有变量的谓词。您尚未提供 table 的定义 - 或具有递归 CTE 的视图。 There is a similar issue with predicate pushing, views, and window functions though.
一种解决方案是将视图更改为接受 mainid 参数的内联 table 值函数,然后将其添加到定义锚点部分的 WHERE
子句中。而不是依赖 SQL 服务器为您推送谓词。
以下查询将在大约 22 秒后 运行:
DECLARE @i INT, @x INT
SET @i = 156567
SELECT
TOP 1
@x = AncestorId
FROM
dbo.tvw_AllProjectStructureParents_ChildView a
WHERE
ProjectStructureId = @i AND
a.NodeTypeCode = 42 AND
a.AncestorTypeDiffLevel = 1
OPTION (RECOMPILE)
问题出在 变量赋值 (确实是这一行:@x = AncestorId
)。删除分配时,它会加速到大约 15 毫秒!
我通过将结果插入临时 table 解决了这个问题,但我认为这是一种糟糕的方法。
任何人都可以帮我解决问题的根源是什么?!
P.S.
错误的执行计划(22s):https://www.brentozar.com/pastetheplan/?id=Sy6a4c9bW
好的执行计划(20ms):https://www.brentozar.com/pastetheplan/?id=Byg8Hc5ZZ
差异可能来自SELECT TOP 1.
当你只有字段时,SQL服务器将只取第一行。当你有变量赋值时 SQL 服务器正在获取所有结果但只使用最前面的结果。
我检查了不同的查询,并不总是这样,但可能在这里 SQL 服务器优化失败,因为 views/tables。
您可以尝试以下解决方法:
DECLARE @i INT, @x INT
SET @i = 156567
SET @x = (SELECT
TOP 1
AncestorId
FROM
dbo.tvw_AllProjectStructureParents_ChildView a
WHERE
ProjectStructureId = @i AND
a.NodeTypeCode = 42 AND
a.AncestorTypeDiffLevel = 1)
当你使用OPTION (RECOMPILE)
SQL服务器时一般可以执行parameter embedding optimisation.
它正在编译的计划是一次性使用的,因此它可以嗅探所有变量和参数的值并将它们视为常量。
下面是一个简单的例子,展示了参数嵌入优化的作用以及分配给变量的效果(未估计实际执行计划)。
DECLARE @A INT = 1,
@B INT = 2,
@C INT;
SELECT TOP (1) number FROM master..spt_values WHERE @A > @B;
SELECT TOP (1) number FROM master..spt_values WHERE @A > @B OPTION (RECOMPILE);
SELECT TOP (1) @C = number FROM master..spt_values WHERE @A > @B OPTION (RECOMPILE);
这方面的计划如下
请注意,中间那个甚至根本没有触及 table,因为 SQL 服务器可以在编译时推断出 @A > @B
不是 true
。但是计划 3 又回到计划中包含 table,因为变量赋值显然阻止了计划 2 中显示的 OPTION (RECOMPILE)
的影响。
(顺便说一句,第三个计划实际上并不是第一个计划的 4-5 倍。分配给变量似乎也抑制了通常的行目标逻辑,在该逻辑中,索引扫描的成本将缩减为反映 TOP 1
)
在你的好计划中,156567
的 @i
值被推入递归 CTE 锚腿中的查找,它返回 0 行,因此递归部分必须不做工作。
在你糟糕的计划中,递归 CTE 完全实现了递归子树的 627,393 次执行,最后将谓词应用于结果 627,393 行(丢弃所有行)
我不确定为什么 SQL 服务器无法下推带有变量的谓词。您尚未提供 table 的定义 - 或具有递归 CTE 的视图。 There is a similar issue with predicate pushing, views, and window functions though.
一种解决方案是将视图更改为接受 mainid 参数的内联 table 值函数,然后将其添加到定义锚点部分的 WHERE
子句中。而不是依赖 SQL 服务器为您推送谓词。