Search Cache For Execution Plans

Posted by & filed under , , , , .

lego matrix pills

Photo credit – pasukaru76

Sometimes when I am working with clients I need to take a look at execution plans for different stored procedures.  Rather than digging around with SQL Profiler or executing the stored procedures with what I think might be the parameters I like to ‘”return to the source”.

Now I feel like I’m making a lame Matrix reference by mentioning the source.  Heck just the mere mention made a neat little picture show up on the blog.

But references aside, going to the execution plan cache is often the best place to go to determine why a stored procedure is performing in a sub-optimal mode.  To accomplish this search, I typically use the T-SQL script below.

All that is needed is to replace the parameter values with the name of the database, schema, and/or stored procedure that needs to be found and it will be returned.  Leaving one or more of the parameters blank will increase the range of the search.


DECLARE @DatabaseName sysname
,@SchemaName sysname
,@ObjectName sysname

SELECT @DatabaseName = ''
,@SchemaName = ''
,@ObjectName = ''

;WITH cteExecInfo (database_name, object_name, execution_count, total_cpu_time, total_physical_reads, total_elapsed_time, sql_text, plan_handle)
AS
(
SELECT DB_NAME(st.dbid) as database_name
,OBJECT_NAME(st.objectid, st.dbid) as object_name
,qs.execution_count
,SUM(qs.total_worker_time)
,SUM(qs.total_physical_reads)
,SUM(qs.total_elapsed_time)
,st.text
,qs.plan_handle
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
WHERE (st.dbid = DB_ID(@DatabaseName) OR DB_ID(@DatabaseName) IS NULL)
AND (st.objectid = OBJECT_ID(QUOTENAME(@DatabaseName) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName))
OR OBJECT_ID(QUOTENAME(@DatabaseName) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@ObjectName)) IS NULL)
GROUP BY st.dbid, st.objectid, qs.execution_count, st.text, qs.plan_handle
)
SELECT cte.database_name
,cte.object_name
,cte.execution_count
,cte.total_cpu_time
,cte.total_physical_reads
,cte.total_elapsed_time
,CAST(cte.total_cpu_time/(cte.execution_count*1.) as decimal(12,2)) as avg_cpu_time
,CAST(cte.total_physical_reads/(cte.execution_count*1.) as decimal(12,2)) as avg_elapsed_time
,CAST(cte.total_elapsed_time/(cte.execution_count*1.) as decimal(12,2)) as avg_elapsed_time
,qp.query_plan
,cte.sql_text
FROM cteExecInfo cte
OUTER APPLY sys.dm_exec_query_plan(cte.plan_handle) qp

The script isn’t exactly perfect, since sys.dm_exec_query_stats aggregates at the statement level there is a good likelihood with some stored procedures will aggregate incorrectly.  If a stored procedure splits between multiple T-SQL statements due to conditional logic (IF statements) each previously executed possible T-SQL statement will be in the DMV.  But I wanted aggregations so that I could make rough estimates of which plans were performing less optimally than others so they are there.  (I feel like I just said nanner, nanner).  Anyways – don’t use this aggregations for reporting.

If anyone finds this useful or has any suggestions, let me know.  For more information on DMVs, take a look at Troubleshooting Performance Problems in SQL Server 2005.  This is the source document that I’ve used over the last year or so to learn about DMVs.