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 服务器为您推送谓词。