Last Actual Plan with sys.dm_exec_query_plan_stats

When you think about execution plans in MS SQL Server you could think that the Estimated query plan are useless. So, here I must disappoint you. All execution plans are estimated. The only difference between the “Actual” and the estimated execution plan is that the so called “Actual” have some added runtime metrics.

Let’s see in the following images examples of estimated and actual execution plans.

Statistics for the estimated plan:

 

Statistics for the “actual” plan:

 

Estimated execution plan:

“Actual" execution plan:

Prior to SQL 2019 to be able to get those metrics it is required that you execute the query.

This is no longer needed in SQL Server 2019 thanks to the newly introduced function “sys.dm_exec_query_plan_stats”. Using this function, you can get the actual plan, the last one run on the system.

To be able to get this plan you must first turn on this feature on the database level (or by using the trace flag 2451) by running this query (example how to turn it on or off):

ALTER DATABASE SCOPED CONFIGURATION SET LAST_QUERY_PLAN_STATS = ON;

ALTER DATABASE SCOPED CONFIGURATION SET LAST_QUERY_PLAN_STATS = OFF;

Once you enabled the feature, you'll begin to see the “actual” execution plan data collected in the Dynamic management view (DMV).

Let’s create a simple test stored procedure and execute it on the system.

CREATE OR ALTER PROCEDURE dbo.LightweightStats
AS
BEGIN
	SELECT 
		COUNT(*) AS Count
	FROM 
		(
			SELECT DISTINCT 
				UnitCost
				, UnitsIn
				, MovementDate 
			FROM 
				dbo.FactProductInventory
		) x;
END
GO

Currently in SQL Server versions prior to 2019 you can get the cached execution plans. Use this query and see what queries are cached:

SELECT 
	ep.usecounts
	, cacheobjtype
	, ep.objtype
	, q.text AS query_text
	, pl.query_plan
	, ep.size_in_bytes
FROM 
	sys.dm_exec_cached_plans ep
	CROSS APPLY sys.dm_exec_sql_text(ep.plan_handle) q
	CROSS APPLY sys.dm_exec_query_plan(ep.plan_handle) pl

If you click on the XML value in the column “query_plan” you will get the estimated execution plan (see earlier image).

So, as I told you, you can get the actual plan (the last run on the system) by using the following query:

SELECT 
	deqps.query_plan
FROM 
	sys.dm_exec_procedure_stats AS deps
	CROSS APPLY sys.dm_exec_query_plan_stats(deps.plan_handle) AS deqps

If you click on the XML value in the column “query_plan”, you will get the actual execution plan (see earlier image).

In case of troubleshooting problems or performance tuning here are some examples where this feature could be very useful:

  • can't execute the real query to be able to generate the actual plans,
  • can't simulate the same environment or
  • can't turn on diagnostics to capture actual plans for every query.

*You can download the complete SQL Script with all examples from the post here: SQL Script.sql (1.20 kb)