3

是否有一个提示可以用来确保当我运行特定查询时优化器不会使用缓存的查询计划?

我找到了强制使用特定计划的MSDN 页面- 但我要求相反。我尝试添加以下提示:

OPTION (USE NO PLAN);

还是我需要实际清除部分缓存?

4

1 回答 1

7

对于单个查询,您可以使用OPTION RECOMPILE 查询提示在每次执行时强制执行新计划。它会是这样的:

SELECT  T.Column1, T2.Column2
FROM    T
        INNER JOIN T2
            ON T.ID = T2.ID
WHERE   T.Column2 = @SomeParameter
OPTION (RECOMPILE);

或者在存储过程级别上,您可以使用WITH RECOMPILE

CREATE PROCEDURE dbo.TestRecompile @Param INT
WITH RECOMPILE
AS
    SELECT  *
    FROM    dbo.T;

如果您想将存储过程从重新编译中标记出来(即下次运行时不使用缓存计划),您可以使用SP_RECOMPILE

EXECUTE sp_recompile 'dbo.ProcedureName';

我不知道 Martin Smith 提到的并发症,我试图重新创建它们但不能(尽管我没有怀疑他,我只是认为我的测试场景太简单了),但是,我想我会无论如何添加结果。

我创建了这个架构:

IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
        DROP TABLE dbo.T;
GO
CREATE TABLE dbo.T 
(   ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, 
    Column1 INT NOT NULL, 
    Column2 INT NULL
);
INSERT dbo.T (Column1, Column2)
SELECT  TOP 9999 1, Number
FROM    Master..spt_values
UNION ALL
SELECT  TOP 1001 Number, Number
FROM    Master..spt_values
WHERE   Type ='P';

CREATE NONCLUSTERED INDEX IX_T_Column1 ON dbo.T (Column1 ASC);

故意对表进行加权,因此 select wherecolumn1 = 1应该使用聚集索引扫描,但所有其他条件都应该使用非聚集索引。控制情况是:

DBCC FREEPROCCACHE;
DECLARE @SQL NVARCHAR(MAX) = 'SELECT COUNT(T.Column2) FROM dbo.T WHERE T.Column1 = @ID';
DECLARE @ParamDef NVARCHAR(MAX) =  N'@ID INT';

EXECUTE SP_EXECUTESQL @SQL, @ParamDef, @ID = 1;
EXECUTE SP_EXECUTESQL @SQL, @ParamDef, @ID = 2;

这产生了两个相同的计划:

在此处输入图像描述

下一个场景是添加OPTION (RECOMPILE)到查询中:

DBCC FREEPROCCACHE;
DECLARE @SQL NVARCHAR(MAX) = '  SELECT  COUNT(T.Column2) 
                                FROM    dbo.T 
                                WHERE   T.Column1 = @ID 
                                OPTION (RECOMPILE);';

DECLARE @ParamDef NVARCHAR(MAX) =  N'@ID INT';

EXECUTE SP_EXECUTESQL @SQL, @ParamDef, @ID = 1;
EXECUTE SP_EXECUTESQL @SQL, @ParamDef, @ID = 2;

这给出了与@ID = 1 的前两个相同的执行计划,但现在使用@ID = 2 的书签查找,这是检索单行时更有效的计划。

在此处输入图像描述

注意,如果我首先使用@ID = 2 执行而不重新编译,两个计划仍然是相同的,但两者都将使用上面显示的@ID = 2 的键查找

另一种选择OPTION (RECOMPILE)是清除特定查询的缓存:

DBCC FREEPROCCACHE;
DECLARE @SQL NVARCHAR(MAX) = '  SELECT  COUNT(T.Column2)
                                FROM    dbo.T 
                                WHERE   T.Column1 = @ID';
DECLARE @ParamDef NVARCHAR(MAX) =  N'@ID INT';

EXECUTE SP_EXECUTESQL @SQL, @ParamDef, @ID = 1;
EXECUTE SP_EXECUTESQL @SQL, @ParamDef, @ID = 2;

DECLARE @PlanHandle VARBINARY(64) = 
                    (   SELECT  TOP 1 PLAN_HANDLE
                        FROM    SYS.DM_EXEC_CACHED_PLANS
                                CROSS APPLY SYS.DM_EXEC_SQL_TEXT(PLAN_HANDLE) AS ST
                        WHERE   ST.TEXT = '(' + @ParamDef + ')' + @SQL
                    );

DBCC FREEPROCCACHE (@PlanHandle);

EXECUTE SP_EXECUTESQL @SQL, @ParamDef, @ID = 2;

在此处输入图像描述 在此处输入图像描述

最初(如控制案例),所有参数值使用相同的计划,但是,您可以清除特定查询定义的缓存,一旦完成,键查找计划将用于@ID = 2;

因此,如果OPTION (RECOMPILE)没有按预期工作,那么您可以使用查询文本的计划句柄来清除该特定查询的缓存。

于 2013-10-02T09:51:49.440 回答