SQL 参数嗅探是否有可能重新编译没有帮助但局部变量有帮助

SQL Parameter sniffing is it possible that recompile does not help but local variables do

我正在使用 sp_executesql 通过一些参数传递一个复杂的选择。这样做比从存储过程中取出并声明变量要慢得多。

我看到很多关于 SQL 参数嗅探的问题,我的情况听起来可能就是这种情况。然而,即使在调用 DBCC FREEPROCCACHE 或用 Option (Recompile) 修改外部 Select 之后,与在存储过程外部编写相同的查询相比,它仍然使用不同且低效的执行计划。

然而,仍然使用存储过程但将参数的副本设置为局部变量将使用有效的执行计划。

这种情况是否排除 SQL 参数嗅探的原因?因为我重新编译查询肯定没有它使用的预先存在的执行计划。如果是这样,造成这种行为的其他原因可能是什么?

只是为了让您了解 sql 查询,您可以在下面看到它(通过 Entity framework 生成的混乱)。这是快速查询,但是当放入 sp_executesql proc 并取出变量并放入参数时,它会生成低效的执行计划

 DECLARE    @p__linq__0 INT = 2032
,@p__linq__1 UNIQUEIDENTIFIER = '8CC161FC-B8DE-4746-BA4F-62FA55DF26DE'
,@p__linq__2 uniqueidentifier= '8CC161FC-B8DE-4746-BA4F-62FA55DF26DE',
@p__linq__3 uniqueidentifier= '8CC161FC-B8DE-4746-BA4F-62FA55DF26DE',
@p__linq__4 uniqueidentifier= '8CC161FC-B8DE-4746-BA4F-62FA55DF26DE',
@p__linq__5 INT = 6771

SELECT 
[Limit1].[UserIdValue] AS [UserIdValue]
FROM   (SELECT [Extent1].[Id] AS [Id]
    FROM [dbo].[Request] AS [Extent1]
    WHERE ([Extent1].[InstanceId] = @p__linq__0) AND ([Extent1].[DeletedDate] IS NULL) AND (( EXISTS (SELECT 
        1 AS [C1]
        FROM ( SELECT 
            [Extent2].[TeamId] AS [TeamId], 
            [Extent2].[HasUpdateAccess] AS [HasUpdateAccess]
            FROM [dbo].[RequestTypeTeam] AS [Extent2]
            WHERE [Extent1].[RequestTypeId] = [Extent2].[RequestTypeId]
        )  AS [Project1]
        WHERE ([Project1].[HasUpdateAccess] = 1) AND ( EXISTS (SELECT 
            1 AS [C1]
            FROM [dbo].[UserTeam] AS [Extent3]
            WHERE ([Project1].[TeamId] = [Extent3].[TeamId]) AND ([Extent3].[UserId] = @p__linq__1)
        ))
    )) OR (([Extent1].[InsertUserId] = @p__linq__2) AND ( EXISTS (SELECT 
        1 AS [C1]
        FROM ( SELECT 
            [Extent4].[TeamId] AS [TeamId], 
            [Extent4].[HasCreatorAccess] AS [HasCreatorAccess]
            FROM [dbo].[RequestTypeTeam] AS [Extent4]
            WHERE [Extent1].[RequestTypeId] = [Extent4].[RequestTypeId]
        )  AS [Project4]
        WHERE ([Project4].[HasCreatorAccess] = 1) AND ( EXISTS (SELECT 
            1 AS [C1]
            FROM [dbo].[UserTeam] AS [Extent5]
            WHERE ([Project4].[TeamId] = [Extent5].[TeamId]) AND ([Extent5].[UserId] = @p__linq__3)
        ))
    ))) OR ( EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[RequestTeam] AS [Extent6]
        WHERE ([Extent1].[Id] = [Extent6].[RequestId]) AND ([Extent6].[TeamId] IN (3147, 3165))
    )) OR ( EXISTS (SELECT 
        1 AS [C1]
        FROM ( SELECT 
            (SELECT 
                COUNT(1) AS [A1]
                FROM [dbo].[RequestControlData] AS [Extent8]
                WHERE ([Project8].[Id] = [Extent8].[ControlId]) AND ([Extent8].[RequestId] = [Extent1].[Id]) AND ([Extent8].[UserIdValue] = @p__linq__4)) AS [C1]
            FROM ( SELECT 
                [Extent7].[Id] AS [Id]
                FROM [dbo].[Control] AS [Extent7]
                WHERE ([Extent7].[RequestTypeId] IS NOT NULL) AND ([Extent1].[RequestTypeId] = [Extent7].[RequestTypeId]) AND ([Extent7].[DeletedDate] IS NULL) AND ([Extent7].[IsAuthorisation] = 1)
            )  AS [Project8]
        )  AS [Project9]
        WHERE [Project9].[C1] > 0
    ))) AND ( NOT ([Extent1].[StatusId] IN (1071))) AND ( NOT ([Extent1].[RequestTypeId] IN (1215)))) AS [Filter11] 
OUTER APPLY  (SELECT TOP (1) 
    [Extent9].[ControlId] AS [ControlId], 
    [Extent9].[UserIdValue] AS [UserIdValue], 
    [Extent10].[Id] AS [Id], 
    [Extent10].[SharedControlId] AS [SharedControlId]
    FROM  [dbo].[RequestControlData] AS [Extent9]
    INNER JOIN [dbo].[Control] AS [Extent10] ON [Extent9].[ControlId] = [Extent10].[Id]
    WHERE ([Filter11].[Id] = [Extent9].[RequestId]) AND (([Extent10].[SharedControlId] = @p__linq__5) OR (([Extent10].[SharedControlId] IS NULL) AND (@p__linq__5 IS NULL
    ))) 
    )
     AS [Limit1]  

我认为你的问题出在设计上而不是数据上,多个子查询更昂贵,尤其是使用 OR 运算符

我宁愿使用多个 table 变量和左外连接

运行 再次遇到同样的问题,想 post 对我有用的答案。

我 运行 UPDATE STATISTICS [TableName] WITH FULLSCAN 在与我的查询相关的各种表上,在此之后它的工作速度与使用局部变量一样快。

有趣的是,如果没有 WITH FULLSCAN,它仍然会很慢。此外,在更新统计数据后,第一个 运行 速度很快,然后在下一个 运行 时变慢,所以我不得不不断更新统计数据以使其快速达到 运行,然后我尝试更新stats,运行查询(很快运行)然后调用DBCC FREEPROCCACHE这似乎修复了它所以每次我运行它

我猜这意味着问题不是参数嗅探。