使用时OPTION (RECOMPILE)
请务必查看执行后(“实际”)计划,而不是执行前(“估计”)计划。一些优化仅在执行时应用:
DECLARE @ForeignKeyCol int = 20;
SELECT ForeignKeyCol, ForeignKeyRank
FROM dbo.ViewOnBaseTable
WHERE ForeignKeyCol = @ForeignKeyCol
OPTION (RECOMPILE);
执行前计划:
执行后计划:
在 SQL Server 2012 build 11.0.3339 和 SQL Server 2008 R2 build 10.50.4270 上测试
背景和限制
在 SQL Server 2005 中添加窗口函数时,优化器无法将选择推过这些新的序列投影。为了解决一些导致性能问题的常见场景,SQL Server 2008 添加了一个新的简化规则 ,SelOnSeqPrj
它允许在值为常量的情况下推送合适的选择。此常量可能是查询文本中的文字,也可能是通过 获取的参数的嗅探值OPTION (RECOMPILE)
。NULLs
尽管查询可能需要ANSI_NULLS OFF
看到这一点,但没有特别的问题。据我所知,仅将简化应用于常量值是实现限制;没有特别的原因不能将其扩展到使用变量。我的回忆是SelOnSeqPrj
规则解决了最常见的性能问题。
参数化
当查询成功自动参数化SelOnSeqPrj
时,不应用该规则。没有可靠的方法来确定查询是否在 SSMS 中自动参数化,它仅表示尝试了自动参数。需要明确的是,占位符 like 的存在仅表明尝试了自动参数化。判断一个准备好的计划是否被缓存以供重用的可靠方法是检查计划缓存,其中“参数化计划句柄”提供了临时计划和准备好的计划之间的链接。[@0]
例如,以下查询在 SSMS 中似乎是自动参数化的:
SELECT *
FROM dbo.ViewOnBaseTable
WHERE ForeignKeyCol = 20;
但计划缓存显示不同:
WITH XMLNAMESPACES
(
DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'
)
SELECT
parameterized_plan_handle =
deqp.query_plan.value('(//StmtSimple)[1]/@ParameterizedPlanHandle', 'nvarchar(64)'),
parameterized_text =
deqp.query_plan.value('(//StmtSimple)[1]/@ParameterizedText', 'nvarchar(max)'),
decp.cacheobjtype,
decp.objtype,
decp.plan_handle
FROM sys.dm_exec_cached_plans AS decp
CROSS APPLY sys.dm_exec_sql_text(decp.plan_handle) AS dest
CROSS APPLY sys.dm_exec_query_plan(decp.plan_handle) AS deqp
WHERE
dest.[text] LIKE N'%ViewOnBaseTable%'
AND dest.[text] NOT LIKE N'%dm_exec_cached_plans%';
如果启用了强制参数化的数据库选项,我们会得到一个参数化结果,其中未应用优化:
ALTER DATABASE Sandpit SET PARAMETERIZATION FORCED;
DBCC FREEPROCCACHE;
SELECT *
FROM dbo.ViewOnBaseTable
WHERE ForeignKeyCol = 20;
计划缓存查询现在显示一个参数化缓存计划,由参数化计划句柄链接:
解决方法
在可能的情况下,我的偏好是将视图重写为内联表值函数,其中可以使选择的预期位置更加明确(如有必要):
CREATE FUNCTION dbo.ParameterizedViewOnBaseTable
(@ForeignKeyCol integer)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
SELECT
bt.PrimaryKeyCol,
bt.ForeignKeyCol,
ForeignKeyRank = DENSE_RANK() OVER (
PARTITION BY bt.ForeignKeyCol
ORDER BY bt.PrimaryKeyCol),
bt.DataCol
FROM dbo.BaseTable AS bt
WHERE
bt.ForeignKeyCol = @ForeignKeyCol;
查询变为:
DECLARE @ForeignKeyCol integer = 20;
SELECT pvobt.*
FROM dbo.ParameterizedViewOnBaseTable(@ForeignKeyCol) AS pvobt;
执行计划: