【问题标题】:SQL Parameter sniffing is it possible that recompile does not help but local variables doSQL 参数嗅探是否可能重新编译无济于事但局部变量有帮助
【发布时间】:2017-01-30 21:57:35
【问题描述】:

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

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

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

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

只是为了让您了解下面的 sql 查询(通过实体框架生成的混乱)。这是快速查询,但是当放入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]  

【问题讨论】:

  • 我不明白如果重新编译没有使用局部变量会有什么帮助。你试过EXECUTE dbo.<procedure name> WITH RECOMPILE;吗?如果性能更好,那么参数嗅探就是问题所在,您可以在过程中考虑OPTIMIZE FOR (UNKNOWN) 查询提示。
  • 我确实尝试过WITH RECOMPILE,但它生成了缓慢的执行计划。刚刚试用了OPTIMIZIE FOR (@myParam UNKNOWN),它生成了快速计划。有趣的是,使用 Optimize for 并传递实际值而不是未知值会产生缓慢的计划。仍然不明白为什么我会遇到这种行为。
  • 您可以尝试比较执行计划以了解发生了什么。您还可以检查程序编译时使用的参数值。
  • 您更新了统计数据吗?这是第一步。
  • 另外,您似乎将 sp_executesql 视为具有“参数嗅探”的存储过程?您实际上是创建了一个新的存储过程,还是只是使用 sp_executesql 运行原始 sql?

标签: sql sql-server stored-procedures parameter-sniffing


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    再次遇到同样的问题,想发布对我有用的答案。

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

    有趣的是,如果没有WITH FULLSCAN,它仍然会很慢。同样在更新统计信息后,第一次运行速度很快,然后下一次运行速度变慢,所以我不得不不断更新统计信息以使其快速运行,然后我尝试更新统计信息,运行查询(快速运行)然后打电话给DBCC FREEPROCCACHE这似乎解决了它所以每次我运行它都很快

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-22
      • 2012-08-11
      • 2021-06-23
      • 2014-09-28
      • 2016-10-07
      • 2016-04-06
      相关资源
      最近更新 更多