【发布时间】: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