Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ensure cold cache when running query

Tags:

sql

sql-server

Is there a hint I can use to ensure that when I run a specific query the optimizer will not use a cached query plan?

I have found the MSDN page for forcing the use of specific plans - but I require the opposite. I tried adding the following hint:

OPTION (USE NO PLAN);

Or will I need to actually clear parts of the cache?

like image 252
whytheq Avatar asked Sep 05 '26 07:09

whytheq


1 Answers

For individual queries you can use the OPTION RECOMPILE query hint to force a new plan with each execution. It would be something like:

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

Or on a stored procedure level you can use WITH RECOMPILE:

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

If as a one off you want to mark a stored procedure from recompilation (i.e. just not use a cached plan the next time it is run) you can use SP_RECOMPILE:

EXECUTE sp_recompile 'dbo.ProcedureName';

I was not aware of the complications Martin Smith mentioned, I tried to recreate them but couldn't (although I don't for a second doubt him, I just think my testing scenario was too simple), but, I thought I'd add the results anyway.

I created this schema:

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);

Deliberately weighting the table so a select where column1 = 1 should use a clustered index scan, but all other conditions should use the nonclustered index. The control case was:

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;

Which yielded two identical plans:

enter image description here

The next scenario was to add OPTION (RECOMPILE) to the query:

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;

This gives the same execution plan as the first two for @ID = 1, but now uses a bookmark lookup for @ID = 2, which is the more efficient plan when retrieving a single row.

enter image description here

N.B. if I had executed with @ID = 2 first with no recompile both plans would still be the same but both would use the key lookup shown above for @ID = 2

Another option instead of OPTION (RECOMPILE) is to clear the cache for a specific query:

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;

enter image description here enter image description here

Initially (like the control case), the same plan is used for all parameter values, however, you can clear the cache for a specific query definition, once this has been done the key lookup plan is used for @ID = 2;

So, if OPTION (RECOMPILE) is not working as expected then you can use the plan handle of the query text to clear the cache for that specific query.

like image 156
GarethD Avatar answered Sep 07 '26 21:09

GarethD



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!