为什么带有和不带变量的相同查询会生成不同的查询计划? 例如,以下查询:
DECLARE @p0 Int = 103
DECLARE @p1 Int = 1
DECLARE @p2 Int = 38
DECLARE @p3 Int = 103
DECLARE @p4 Int = 1
SELECT [t5].[pkCompanyID] AS [CompanyID], [t5].[name] AS [Name], [t5].[imageurl] AS [ImageURL]
FROM (
SELECT [t0].[pkCompanyID], [t0].[name], [t1].[imageurl],
(CASE
WHEN EXISTS(
SELECT NULL AS [EMPTY]
FROM [tblCompany] AS [t2]
WHERE ([t2].[fkCompToCompID] = ([t0].[pkCompanyID])) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [tblUserToGroupToCompany] AS [t3]
INNER JOIN [tblGroupToApplication] AS [t4] ON [t3].[fkGroupID] = [t4].[fkGroupID]
WHERE ([t3].[fkCompanyID] = [t2].[pkCompanyID]) AND ([t3].[fkUserID] = @p0) AND ([t4].[fkApplicationID] = @p1)
))
) THEN 1
ELSE 0
END) AS [value], [t0].[fkCompToCompID]
FROM [tblCompany] AS [t0]
LEFT OUTER JOIN [tblNodeTypes] AS [t1] ON [t1].[pkNodeTypeID] = [t0].[fkNodeTypeID]
) AS [t5]
WHERE (([t5].[value] = 1) OR (EXISTS(
SELECT NULL AS [EMPTY]
FROM [tblUserToGroupToCompany] AS [t6]
WHERE [t6].[fkCompanyID] = [t5].[pkCompanyID]
))) AND ([t5].[fkCompToCompID] = @p2) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [tblUserToGroupToCompany] AS [t7]
INNER JOIN [tblGroupToApplication] AS [t8] ON [t7].[fkGroupID] = [t8].[fkGroupID]
WHERE ([t7].[fkCompanyID] = [t5].[pkCompanyID]) AND ([t7].[fkUserID] = @p3) AND ([t8].[fkApplicationID] = @p4)
))
生成此计划(计划的一部分)
但是,如果我直接在查询中交换值的变量,则相同的查询。 例如
...WHERE ([t7].[fkCompanyID] = [t5].[pkCompanyID]) AND ([t7].[fkUserID] = 103) AND ([t8].[fkApplicationID] = 1)
生成此计划(与其他计划相同)
计划中还有其他变化,但我不能适应整个图像。 第一个查询比第二个查询快50%。
答案 0 :(得分:2)
因为当您使用FIXED值并关闭AUTO-PARAMETERIZATION时,查询计划会完全知道运行查询所需的值。因此,该计划特别针对这些值进行了调整。
但是,当您使用变量时,将放入查询缓存的计划是包含参数化变量的计划 - 可以由任何变量替换,并将重新使用相同的计划。因此,这些计划必须更加强大和通用,以处理“最佳平均情况”。
在SQL Server 2008中,您可以设置是否自动参数化简单参数,以便始终获得“最佳平均案例”计划 - 包括所有好处和坏处。
价:
至于性能,优化器可能会出错 - 在这种情况下,它会查看统计信息for the exact values
,并认为索引没有帮助(可能是一个临界点问题),因此该计划适用于群集扫描。关于是否强制执行查询计划是一件很好的事情 - 但显然使用索引的速度要快50%
除非我有一个非常好的理由,否则我不会游戏查询优化器(例如使用索引提示),除非我有大量数据证明它总是会使它更快。