3

以下查询将在大约 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 毫秒!我通过将结果插入临时表来解决它,但我认为这是一个不好的方法。

谁能帮我解决问题的根源是什么?!

附言

糟糕的执行计划(22):https ://www.brentozar.com/pastetheplan/?id=Sy6a4c9bW

良好的执行计划(20毫秒):https ://www.brentozar.com/pastetheplan/?id=Byg8Hc5ZZ

4

2 回答 2

4

使用OPTION (RECOMPILE)SQL Server 时一般可以进行参数嵌入优化

它正在编译的计划是一次性使用的,因此它可以嗅探所有变量和参数的值并将它们视为常量。

下面是一个简单的示例,显示了参数嵌入优化的实际效果以及分配给变量的效果(未估计实际执行计划)。

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);

计划如下

在此处输入图像描述

请注意,中间的甚至根本不触及表,因为 SQL Server 可以在编译时推断出@A > @B不是true. 但是计划 3 又将表包含在计划中,因为变量赋值显然阻止了OPTION (RECOMPILE)计划 2 中显示的效果。

(顺便说一句,第三个计划实际上并不像第一个那样昂贵 4-5 倍。分配给变量似乎也抑制了通常的行目标逻辑,其中索引扫描的成本将按比例缩小以TOP 1反映

在您的好计划中, 的@i156567被推入递归 CTE 的锚腿中的搜索,它返回 0 行,因此递归部分必须不做任何工作。

在此处输入图像描述

在您的错误计划中,递归 CTE 完全实现了递归子树的 627,393 次执行,最后将谓词应用于最终的 627,393 行(丢弃所有行)

在此处输入图像描述

我不确定为什么 SQL Server 不能将带有变量的谓词下推。您尚未提供表的定义 - 或具有递归 CTE 的视图。不过,谓词推送、视图和窗口函数也存在类似的问题

一种解决方案是将视图更改为内联表值函数,该函数接受 mainid 的参数,然后将其添加到WHERE定义的锚部分的子句中。而不是依靠 SQL Server 为您下推谓词。

于 2017-05-30T17:31:14.353 回答
-2

差异可能来自 SELECT TOP 1。

当您只有字段时,SQL Server 将只占用第一行。当您进行变量赋值时,SQL Server 会获取所有结果,但只使用最上面的结果。

我检查了不同的查询,但情况并非总是如此,但由于视图/表的复杂性,SQL Server 优化可能在这里失败。

您可以尝试以下解决方法:

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)
于 2017-05-30T07:58:22.507 回答