Obtaining Statement-Level Query Plans
In workloads that involve expensive long-running queries and reports, or complex batch operations, you frequently want to see the SQL statements and associated query plans that are active on the system or that consume the most resources.
SQL Server 2005 makes available the query plans and SQL text associated with running queries or cached statements through the management views sys.dm_exec_requests and sys.dm_exec_query_stats respectively. Both of these views provide a plan_handle for the XML plan and a sql_handle for the query text, along with resource consumption and execution information. To view the plan or SQL text, you use the table-valued functions sys.dm_exec_query_plan(<plan_handle>) and sys.dm_exec_sql_text(<sql_handle>).
However, the plans and text returned for these handles include all of the queries in the SP or batch, rather than an individual sql statement or plan. Yet the views sys.dm_exec_requests and sys.dm_exec_query_stats contain a separate entry for each statement within an SP or Batch, along with metrics about that statement.
So how do you capture the individual statement and plan corresponding to a specific row in these tables? The only link provided in these tables to identify the SQL statement that is running, or a cached query of interest within the SP or Batch, are the columns statement_start_offset and statement_end_offset which isolate the substring containing the individual statement’s SQL.
Fortunately, using the XML parsing capabilities built-in to SQL2005, we can split a multi-statement query plan into separate fragments for each statement in a batch or SP. From each of these fragments we can also extract the corresponding SQL query string of the statement it applies to – since each plan fragment contains the SQL text as an element. And then we can join this single-statement SQL text to the substring identified by the offsets I mentioned above to retrieve just the plan of the specific SQL statement of interest.
To illustrate this parsing, consider the following table-value function that partially shreds sys.dm_exec_query_plan to generate a separate row for each SQL statement in a batch or SP. Each row contains both the SQL Text and Query Plan of statements for a given plan_handle : (Note: This function requires CTP16 or later versions of SQL2005)
-- statement_level_query_plan.sql ***********************************************
--
-- v1.3
-- Stuart Ozer
-- Microsoft Corporation
-- Provided AS-IS with no warranties
--
--
CREATE FUNCTION statement_level_query_plan(
@handle as varbinary(64) -- Handle for the overall query plan
)
RETURNS TABLE as
RETURN (
select
statement_nbr, -- Sequential number of statement within batch or SP
statement_type, -- SELECT, INSERT, UPDATE, etc
statement_subtree_cost, -- Estimated Query Cost
statement_estimated_rows, -- Estimated Rows Returned
statement_optimization_level, -- FULL or TRIVIAL
statement_text, -- Text of query
statement_plan -- XML Plan To view as a graphical plan
-- save the column output to a file with extension .SQLPlan
-- then reopen the file by double-clicking
from (
select
C.value('@StatementId','int') as statement_nbr,
C.value('(./@StatementText)','nvarchar(max)') as statement_text,
C.value('(./@StatementType)','varchar(20)') as statement_type,
C.value('(./@StatementSubTreeCost)','float') as statement_subtree_cost,
C.value('(./@StatementEstRows)','float') as statement_estimated_rows,
C.value('(./@StatementOptmLevel)','varchar(20)') as statement_optimization_level,
-- Construct the XML headers around the single plan that will permit
-- this column to be used as a graphical showplan.
-- Only generate plan columns where statement has an associated plan
C.query('declare namespace PLN="https://schemas.microsoft.com/sqlserver/2004/07/showplan";
if (./PLN:QueryPlan or ./PLN:Condition/PLN:QueryPlan)
then
<PLN:ShowPlanXML><PLN:BatchSequence><PLN:Batch><PLN:Statements><PLN:StmtSimple>
{ ./attribute::* }
{ ./descendant::PLN:QueryPlan[1] }
</PLN:StmtSimple></PLN:Statements></PLN:Batch></PLN:BatchSequence></PLN:ShowPlanXML>
else ()
') as statement_plan
from
sys.dm_exec_query_plan(@handle)
CROSS APPLY
-- This expression finds all nodes containing attribute StatementText
-- regardless of how deep they are in the potentially nested batch hierarchy
-- The results of this expression are processed by the Select expressions above
query_plan.nodes('declare namespace PLN="https://schemas.microsoft.com/sqlserver/2004/07/showplan";
/PLN:ShowPlanXML/PLN:BatchSequence/PLN:Batch/PLN:Statements/descendant::*[attribute::StatementText]')
as T(C)
) x
)
Besides delivering columns containing the statement-level text and XML plan, the function also extracts other useful information as columns such as estimates for the query cost and expected rowcount.
To use the function to view query plans, statements and execution details for all currently executing queries, we join the above function to the executing SQL text substrings obtained from sys.dm_exec_requests. The join is a bit complex because we have to take into account the fact that query strings embedded in the plan may have some spurious leading characters, and is also truncated while the query in sys.dm_exec_requests is not. So, the query to report all executing SQL statements and their plans is:
select pln.*, req.* from sys.dm_exec_requests as req
CROSS APPLY statement_level_query_plan(plan_handle) as pln
where statement_text like
'%' +
replace(
left(
substring((select text from master.sys.dm_exec_sql_text(sql_handle)),
Comments
Anonymous
January 19, 2007
Please check out http://blogs.msdn.com/sqlprogrammability/ for a series of articles on the Plan Cache and improvements made to the plan cache in SQL Server 2005 SP2. The articles also have a number of examples and queries on plan cache related DMVs that you may find useful.Anonymous
July 10, 2008
PingBack from http://irvin.onlinevidsworld.info/obtainingstatements.htmlAnonymous
July 10, 2008
PingBack from http://nyasia.supervidsdigest.info/obtainingstatement.htmlAnonymous
June 01, 2009
PingBack from http://portablegreenhousesite.info/story.php?id=14095Anonymous
June 16, 2009
PingBack from http://fixmycrediteasily.info/story.php?id=15901Anonymous
June 18, 2009
PingBack from http://wheelbarrowstyle.info/story.php?id=1372Anonymous
June 18, 2009
PingBack from http://thestoragebench.info/story.php?id=7277