I'm trying to write a query to find information about query stats and the query plan for a specific stored procedure, but I can't manage to find the right DMV or query to look for a specific stored procedure.
So far I have:
select
qs.sql_handle
, qs.statement_start_offset
, qs.statement_end_offset
, qs.plan_handle
, execution_count
, st.text
, substring(st.text, (qs.statement_start_offset/2)+1,
((case qs.statement_end_offset
when -1
then datalength(st.text)
else
qs.statement_end_offset
end - qs.statement_start_offset) / 2 + 1)) as [Filtered text]
, qp.query_plan
from sys.dm_exec_query_stats as qs
cross apply sys.dm_exec_sql_text (qs.sql_handle) as st
cross apply sys.dm_exec_query_plan (qs.plan_handle) as qp
where st.text like '%myProcedure%'
order by qs.sql_handle
, execution_count desc
But this query doesn't return any information. Ideally I would replace the st.text like
condition with something like object_name(procedure_id) = 'myProcedure'
, but I can't find the right way to do so.
Is there a better way to do so?
I could replace the condition in the WHERE
clause with a qs.sql_handle = 0x000004004040400..etc.
but how can I find out the sql_handle
for the procedure? (this information isnt' found in sys.objects
or in sys.procedures
).
Any information is greatly appreciated.
Update:
I've executed the procedure (exec myProcedure ...
) in a test environment and now it is returned by the above query and so I can get the plan handle to use the where sql_handle = 0x0034300..
, but I'd like to run the same query from above in production, without having to run the procedure in production (just to find out its plan handle).
This is the reason why I'm trying to write the query in this way, to get the information based on name, not sql_handle
(just because in prod I don't know what is its sql_handle
).
Best Answer
Given that the
sys.dm_exec_query_stats
DMV returns data for the entire instance, you need to be able to get object Schema and name info across all Databases. UsingST.objectid = OBJECT_ID(N'dbo.ProcedureName')
, as has been suggested, is error-prone since it will only resolve the name relative to the database in which the query is being executed:NULL
, which will filter out all rows.object_id
value that might be correct, but could just as well point to a different object and might return one or more incorrect rows.object_id
value is returned, whether correct or incorrect, that particular value can exist across multiple databases and so you might get multiple rows back for different objects in different databases.At the very least, you would need to provide a fully-qualified object name (i.e. three-part name). However, even if you do use
OBJECT_ID(N'DatabaseName.SchemaName.ObjectName')
the query still has the potential to run into problem #3 noted above since thatobject_id
, even being correct, can exist across multiple Databases. So you need a second condition using theDB_ID()
function to narrow down to the intended Database.HOWEVER, my preference is to use the OBJECT_NAME and OBJECT_SCHEMA_NAME functions as they both accept an optional 2nd parameter for
database_id
. The advantage here is that you can use them in theSELECT
clause when you are not narrowing down to one specific object, so that you can see the object names for all of the rows in the DMV. It also allows for filtering down to the same object across multiple Databases, which is useful when you have the same Stored Procedure in multiple Databases and want to see rows for it across all Databases, even though it likely has differentobject_id
values across those Databases.Hence, you could optionally add the following to the
SELECT
clause (I use this when Cross Applyingsys.dm_exec_sql_text
to DMVs such assys.dm_exec_requests
, etc):And you would then update your query to have the following
WHERE
clause: