{"id":68773,"date":"2016-10-18T14:02:33","date_gmt":"2016-10-18T14:02:33","guid":{"rendered":"https:\/\/www.simple-talk.com\/?p=68773"},"modified":"2021-08-24T13:39:28","modified_gmt":"2021-08-24T13:39:28","slug":"retrieving-sql-server-query-execution-plans","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/retrieving-sql-server-query-execution-plans\/","title":{"rendered":"Retrieving SQL Server Query Execution Plans"},"content":{"rendered":"<p>One of the most useful tools in SQL Server for analyzing and troubleshooting a query\u2019s performance is the execution plan. An execution plan can tell you how the database engine intends to execute a query, as determined by the query optimizer, or how the database engine actually executed the query.<\/p>\n<p>In most cases, the estimated plan and actual plan are one in the same. The optimizer generates the plan and hands the plan off to the engine, which uses it executes the query. When you view the actual plan, you\u2019re essentially looking at the estimated plan generated by the optimizer, with a few runtime statistics added in for good measure.<\/p>\n<p>The actual plan will differ from estimated plan only if something happens in between the estimating and processing operations that forces SQL Server to recompile the associated query and the optimizer to produce a completely new plan. For example, someone might modify an underlying table or recompile a stored procedure.<\/p>\n<p>The execution plan breaks the execution process down into hierarchical steps that show each operation needed to access the target data or perform computations against that data. For each operation, the execution plan provides detailed information such as the operation type, output columns, number of rows, and estimated CPU and I\/O usage. The details also include the estimated cost of an operation, as determined by the optimizer, relative to the rest of the query\u2019s operations.<\/p>\n<p>SQL Server provides a number of methods for accessing a query\u2019s execution plan, ranging from clicking a button in SQL Server Management Studio (SSMS) to setting up extended events that capture execution plans based on specific criteria. In this article, we look at the various methods for accessing execution plans and provide some of the necessary basics for how to find the information they provide.<\/p>\n<h2>Using SSMS to access execution plans<\/h2>\n<p>The simplest and quickest way to access a query\u2019s execution plan is to use SSMS to view a graphical representation of either the estimated or actual plan. To demonstrate how this works, let\u2019s start with the following <strong>SELECT<\/strong> statement, which joins two tables in the <strong>AdventureWorks2014<\/strong> database (installed on a local instance of SQL Server 2014):<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT sh.SalesOrderID, sh.CustomerID, sh.OrderDate, \r\n  sd.ProductID, sd.LineTotal\r\nFROM Sales.SalesOrderHeader sh JOIN Sales.SalesOrderDetail sd \r\n  ON sh.SalesOrderID = sd.SalesOrderID \r\nWHERE sd.LineTotal BETWEEN 1000 AND 1500;\r\n<\/pre>\n<p>The <strong>SELECT<\/strong> statement simply joins the <strong>SalesOrderHeader<\/strong> table with the <strong>SalesOrderDetail<\/strong> table and returns a range of values based on the <strong>LineTotal<\/strong> column in the <strong>SalesOrderDetail<\/strong> table. The <strong>LineTotal<\/strong> column is a computed column that derives its values from the <strong>UnitPrice<\/strong>, <strong>UnitPriceDiscount<\/strong>, and <strong>OrderQty<\/strong> columns in the same table.<\/p>\n<p>To display a query\u2019s execution plan, you must first type (or copy-and-paste) the query into a new query tab in SSMS or open a T-SQL script file that contains the query. You can also type the query into an existing tab that contains other queries, but if you do, you must highlight the query before trying to view the plan, just as you would if running a single T-SQL statement from a group of statements.<\/p>\n<p>With the query in place, and selected if necessary, click the <strong>Display<\/strong> <strong>Estimated<\/strong> <strong>Execution<\/strong> <strong>Plan<\/strong> button on the SSMS toolbar to view its estimated plan, or click the <strong>Include<\/strong> <strong>Actual<\/strong> <strong>Execution<\/strong> <strong>Plan<\/strong> button to view the actual plan.<\/p>\n<p>The <strong>Display<\/strong> <strong>Estimated<\/strong> <strong>Execution<\/strong> <strong>Plan<\/strong> button is a one-time event, which means you must click it each time you want to view the estimated plan for a selected query. If you select this option, the database engine generates the plan without running the query.<\/p>\n<p>The <strong>Include<\/strong> <strong>Actual<\/strong> <strong>Execution<\/strong> <strong>Plan<\/strong> button is a toggle that is either on or off. If on, the database engine executes the query and generates an actual plan. SSMS will continue to return the execution plan for each executed query until you explicitly toggle the button off.<\/p>\n<p>Regardless of which option you choose, SSMS displays the plan on its own results tab, separate from any query results or messages. The following figure shows the execution plan after turning the <strong>Include<\/strong> <strong>Actual<\/strong> <strong>Execution<\/strong> <strong>Plan<\/strong> option on.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/10\/word-image-8.png\" alt=\"%20screenshots\/stExecPlans_fig01.png\" \/><\/p>\n<p>In this case, the actual plan displayed is the same as the estimated plan, which indicates that the optimizer likely did not generate a new plan when it came time to processing the query. More often than not, this will be the situation you\u2019ll run into, particularly for simple queries such as our example. That said, there will be times when they differ. For example, if a table\u2019s statistics are not current as a result of inserting data into an <strong>IDENTITY<\/strong> column, SQL Server will update the statistics next time you run a query against that table and then recompile the plan.<\/p>\n<p>Whether you\u2019re working with the estimated plan or the actual plan, SSMS presents the information in a similar manner, displaying a specific icon, or operator, for each operation type. The execution plan for our sample <strong>SELECT<\/strong> query shows five operations, but only three different types:<\/p>\n<ul>\n<li><strong>Merge<\/strong> <strong>Join:<\/strong> Performs a type of join, such as an inner join, left outer join, or right outer join. In this case, the operation is performing an inner join on the two data sets from the <strong>SalesOrderHeader<\/strong> and <strong>SalesOrderDetail<\/strong> tables.<\/li>\n<li><strong>Clustered<\/strong> <strong>Index<\/strong> <strong>Scan:<\/strong> Scans a clustered index and returns all rows or only those rows specified in the <strong>WHERE<\/strong> clause. For the <strong>SalesOrderHeader<\/strong> table, the operation returns all rows. For the <strong>SalesOrderDetail<\/strong> table, the operation returns only those row with a <strong>LineTotal<\/strong> value between <strong>1000<\/strong> and <strong>1500<\/strong>.<\/li>\n<li><strong>Compute<\/strong> <strong>Scalar:<\/strong> Evaluates an expression and returns a computed scalar value. In our example, both <strong>Compute<\/strong> <strong>Scalar<\/strong> operations are related to calculating the <strong>LineTotal<\/strong> value for each row returned by the bottom <strong>Clustered<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation.<\/li>\n<\/ul>\n<p>Not surprisingly, SSMS supports numerous other icons to represent the various operation types, nearly 90 at last count. You can find a list of icons in the TechNet topic <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms175913(v=sql.105).aspx\">Graphical Execution Plan Icons (SQL Server Management Studio)<\/a>. The topic also provides links to individual descriptions of each operation.<\/p>\n<p>One icon I have not mentioned and one you will not find on the TechNet list is <strong>SELECT<\/strong>. This is because it is not considered an operation. Rather, the icon represents the final results of the query. It sits at the top of the plan hierarchy and provides information about the query as a whole.<\/p>\n<p>We\u2019ll get into how to view more details about the <strong>SELECT<\/strong> icon and the other nodes shortly, but first let\u2019s take a step back to look at the plan as a whole because this is where things can sometimes get confusing, especially if you\u2019re new to execution plans.<\/p>\n<p>When reading an execution plan, you generally move from right-to-left and from top-to-bottom, so from this perspective, the first operation is the <strong>Clustered Index Scan<\/strong> against the <strong>SalesOrderHeader<\/strong> table, the second operation is the <strong>Clustered Index Scan<\/strong> against the <strong>SalesOrderDetail<\/strong> table, the third operation is the <strong>Compute<\/strong> <strong>Scalar<\/strong> calculation on the <strong>LineTotal<\/strong> column, and so on. In this way, the execution plan shows the order in which operations occur. The arrows between the operators indicate the direction that the data flows, sized proportionately based on the amount of data moving from one operation to the next.<\/p>\n<p>SSMS also shows the execution plan as a hierarchy, based on the execution order, with the <strong>SELECT<\/strong> node representing the top level of that hierarchy (as well as the query\u2019s final output). From the <strong>SELECT<\/strong> node, you go from left-to-right to move down the hierarchy. So the first level down the hierarchy is the <strong>Merge<\/strong> <strong>Join<\/strong> operation that joins the data from the two data flow streams.<\/p>\n<p>The next level down includes both the top <strong>Clustered<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation and the left <strong>Compute<\/strong> <strong>Scalar<\/strong> operation, both of which act as children to the <strong>Merge<\/strong> <strong>Join<\/strong> parent. The left <strong>Compute<\/strong> <strong>Scalar<\/strong> operation also serves as the parent of the right <strong>Compute<\/strong> <strong>Scalar<\/strong> operation, and the right <strong>Compute<\/strong> <strong>Scalar<\/strong> operation serves as the parent for the bottom <strong>Clustered<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation.<\/p>\n<p>Consistent with the execution hierarchy, SSMS numbers the operations (referred to as the node IDs) from left-to-right, using a 0-based system:<\/p>\n<ul>\n<li>The <strong>Merge<\/strong> <strong>Join<\/strong> operation is node 0.<\/li>\n<li>The top <strong>Cluster<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation is node 1.<\/li>\n<li>The left <strong>Compute<\/strong> <strong>Scalar<\/strong> operation is node 2.<\/li>\n<li>The right <strong>Compute<\/strong> <strong>Scalar<\/strong> operation is node 3.<\/li>\n<li>The bottom <strong>Cluster<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation is node 4.<\/li>\n<\/ul>\n<p>Along with each operator icon, SSMS also provides the <strong>Cost<\/strong> percentage, which is the estimated cost of the operation compared to the other operations. If you were to add the percentages together for all the operations, they would total 100%.<\/p>\n<p>The percentages provide a quick way to see which operations the optimizer thinks will eat up the bulk of your workload. For example, this execution plan indicates that the bottom <strong>Cluster<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation (node 4) has an estimated operator cost of 58%, representing a significant portion of the query\u2019s processing. If you were troubleshooting a query, that would be a good place to start. Keep in mind, however, that these costs can sometimes be misleading. For example, if you call a user-defined function (UDF) within a query, the cost of executing the UDF is <em>hidden<\/em> in a <strong>Compute<\/strong> <strong>Scalar<\/strong> value of <strong>0%<\/strong>.<\/p>\n<p>You can find additional information about each operation by hovering over the icon until a pop-up window appears that displays the specific details. For example, the following figure shows the pop-up window that appears when hovering over the bottom <strong>Clustered<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/10\/word-image-9.png\" alt=\"%20screenshots\/stExecPlans_fig02.png\" \/><\/p>\n<p>As you can see, we now have a significant amount of information about the operation. For example, the <strong>Physical<\/strong> <strong>Operation<\/strong> property shows the operation that the database engine carried out in performing the query, and the <strong>Logical<\/strong> <strong>Operation<\/strong> property shows the operation that the optimizer calculated as the best operation to use. Often these two will be the same, but sometimes they will differ, depending on the type of operation. For example, the <strong>Merge<\/strong> <strong>Join<\/strong> shows the <strong>Physical<\/strong> <strong>Operation<\/strong> property as <strong>Merge<\/strong> <strong>Join<\/strong>, but shows the <strong>Logical<\/strong> <strong>Operation<\/strong> property as <strong>Inner<\/strong> <strong>Join<\/strong>, a more precise description of the type of join.<\/p>\n<p>The pop-up window also provides information about how the operation was processed and about the data itself. For a description of each one, see the TechNet article <a href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms178071(v=sql.105).aspx\">Displaying Graphical Execution Plans (SQL Server Management Studio)<\/a>.<\/p>\n<p>You can view even more information about each operation by accessing the operation\u2019s properties. To do so, right click the specific icon, and then click <strong>Properties<\/strong>. This opens the <strong>Properties<\/strong> pane, where you can dig into an assortment of information about the operation, as shown in the following figure.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/10\/word-image-10.png\" alt=\"%20screenshots\/stExecPlans_fig04.png\" \/><\/p>\n<p>You can also retrieve information about the data flow itself by hovering over the data flow paths (arrows). For example, the following figure shows the pop-up window that appears when hovering over the data flow path between the bottom <strong>Clustered<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation and the right <strong>Compute<\/strong> <strong>Scalar<\/strong> operation.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/10\/word-image-11.png\" alt=\"%20screenshots\/stExecPlans_fig03.png\" \/><\/p>\n<p>Here you can get a quick glimpse of the amount of data moving from one operation to the next and what the estimates were for those rows. In this way, you can see whether the actual row count differs significantly from the estimates. You can also see whether the number of rows is very different from what you expected, which might in itself point to a problem.<\/p>\n<p>Verifying these row counts can be the biggest reason to reviewing the actual plan. If the optimizer thought 10 rows would be returned, but it was actually 100,000 rows, you have a very inefficient plan, and you need to find out why the optimizer got confused (for example, as a result bad statistics) or what other reason might have caused the disparity, such as parameter-sniffing problems.<\/p>\n<p>SSMS also adjusts the sizes of the data path arrows themselves to indicate the amounts of data moving from one operation to the next. In this way, you might be able to get a quick hint that there is a problem with the related operations. For example, if the plan shows fat arrows for most of the data flow and then suddenly is passing along only a few rows, you have a way to pinpoint where there might be an issue with your query.<\/p>\n<p>At the top of the execution plan, you\u2019ll also see a partial note about a missing index. To view specifics about the index, right-click the execution plan and then click <strong>Missing<\/strong> <strong>Index<\/strong> <strong>Details<\/strong>. SSMS opens a new tab and displays the following information:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">\/*\r\nMissing Index Details from stExecPlans.sql - localhost\\sqlsrv2014.AdventureWorks2014 (WIN7AVM\\Administrator (53))\r\nThe Query Processor estimates that implementing the following index could improve the query cost by 56.9646%.\r\n*\/\r\n\r\n\/*\r\nUSE [AdventureWorks2014]\r\nGO\r\nCREATE NONCLUSTERED INDEX [&lt;Name of Missing Index, sysname,&gt;]\r\nON [Sales].[SalesOrderDetail] ([LineTotal])\r\nINCLUDE ([SalesOrderID],[ProductID])\r\nGO\r\n*\/\r\n<\/pre>\n<p>The message is related to the <strong>SalesOrderDetail<\/strong> table, which is the target of the bottom <strong>Clustered<\/strong> <strong>Index<\/strong> <strong>Scan<\/strong> operation. We already had a clue that there might be an issue with this operation because of the associated <strong>Cost<\/strong> value of 58%. However, this message takes it a step further and recommends creating a nonclustered index on the <strong>LineTotal<\/strong> column (something you can do for only certain computed columns).<\/p>\n<p>From this information, you might decide that such an index is a good idea, or you might determine that the query is not used often enough or on large enough data sets to warrant the overhead of an extra index. For example, if the scan shows a table cardinality of 12,000 rows and the query return 7,500 rows, the extra index is probably not worth it. However, if the cardinality shows a million rows, but the query still returns only 7,500 rows, you might consider the index, depending on the frequency and importance of the query.<\/p>\n<p>On the other hand, you might choose to create a covering index that is based on the <strong>SalesOrderID<\/strong> column but includes the <strong>LineTotal<\/strong> column.<\/p>\n<p>When working with execution plans, you might also find it useful to review the <strong>SELECT<\/strong> node, the node at the top of the query hierarchy. In addition to showing the original query on which the plan is based, the <strong>SELECT<\/strong> node includes the following properties:<\/p>\n<ul>\n<li><strong>Cached<\/strong> <strong>plan<\/strong> <strong>size<\/strong> <strong>\u2013<\/strong> Amount of memory the plan uses is in the plan cache.<\/li>\n<li><strong>Degree<\/strong> <strong>of<\/strong> <strong>Parallelism<\/strong> <strong>\u2013<\/strong> Number of processors used to run the query.<\/li>\n<li><strong>Estimated<\/strong> <strong>Operator<\/strong> <strong>Cost<\/strong> <strong>\u2013<\/strong> Estimated optimizer cost for running this specific operator.<\/li>\n<li><strong>Estimated<\/strong> <strong>Subtree<\/strong> <strong>Cost<\/strong> <strong>\u2013<\/strong> Accumulated estimated optimizer cost for all steps in the query.<\/li>\n<li><strong>Estimated<\/strong> <strong>Number<\/strong> <strong>of<\/strong> <strong>Rows<\/strong> <strong>\u2013<\/strong> Estimated number of rows affected by the query.<\/li>\n<\/ul>\n<p>As with any type of optimization, it depends on your particular circumstances. The execution plan is there to point out where problems might exist, and in this case actually offers a possible solution, but ultimately you must decide how best to address any issues that the execution plan exposes.Retrieving an execution plan as XML.<\/p>\n<p>As handy as the graphical execution plan is for examining how a query is being executed, there might be times when you want the information in a format that you can use with other tools, easily save for later analysis, or share with other administrators and developers.<\/p>\n<p>To this end, SSMS lets you view\u2014and subsequently save\u2014the execution plan as an XML file. To do so, right-click the execution plan and then click <strong>Show<\/strong> <strong>Execution<\/strong> <strong>Plan<\/strong> <strong>XML<\/strong>. SSMS opens a new tab and displays the XML. The following figure shows a small portion of the XML, which, although not readily apparent, conforms to the same operational hierarchy we saw in the graphical execution plan.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/10\/word-image-12.png\" alt=\"%20screenshots\/stExecPlans_fig06.png\" \/><\/p>\n<p>The XML provides all the information available about the query plan in a single document, which you can save and then analyze at your convenience, using your tools of choice. Although the XML structure is much more complex than we can go into here, you can get a good sense of how the pieces fit together by generating the XML and examining it. You can also reference the schema that the XML is written against by going to <a href=\"http:\/\/schemas.microsoft.com\/sqlserver\/2004\/07\/showplan\">http:\/\/schemas.microsoft.com\/sqlserver\/2004\/07\/showplan<\/a>.<\/p>\n<p>To help you get started with the XML, there a couple items worth noting. First, the element that primarily concerns us in our example plan is <strong>&lt;StmtSimple&gt;<\/strong>, which specifies the statement text, the applicable <strong>SET<\/strong> options, and a few other details about the statement. Within this element, you\u2019ll also find the <strong>&lt;QueryPlan&gt;<\/strong> element, which is where we get into the meat of the execution plan.<\/p>\n<p>The <strong>&lt;QueryPlan&gt;<\/strong> element contains a series of <strong>&lt;RelOp&gt;<\/strong> elements. Each <strong>&lt;RelOp&gt;<\/strong> element represents one of the plan\u2019s operations, adhering to the same hierarchical structure as that of the graphical execution plan. The following XML shows an abbreviated overview of the <strong>&lt;RelOp&gt;<\/strong> elements that define the plan:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">&lt;StmtSimple&gt;\r\n  &lt;QueryPlan&gt;\r\n    &lt;RelOp PhysicalOp=\"Merge Join\" NodeId=\"0\"&gt;\r\n      &lt;RelOp PhysicalOp=\"Cluster Index Scan\" NodeId=1\"&gt;\r\n      &lt;\/RelOp PhysicalOp=\"Cluster Index Scan\" NodeId=1\"&gt;\r\n      &lt;RelOp PhysicalOp=\"Compute Scalar\" NodeId=2\"&gt;\r\n        &lt;RelOp PhysicalOp=\"Compute Scalar\" NodeId=3\"&gt;\r\n          &lt;RelOp PhysicalOp=\"Compute Scalar\" NodeId=4\"&gt;\r\n          &lt;\/RelOp PhysicalOp=\"Compute Scalar\" NodeId=4\"&gt;\r\n        &lt;\/RelOp PhysicalOp=\"Compute Scalar\" NodeId=3\"&gt;\r\n      &lt;\/RelOp PhysicalOp=\"Compute Scalar\" NodeId=2\"&gt;\r\n    &lt;\/RelOp PhysicalOp=\"Merge Join\" NodeId=\"0\"&gt;\r\n  &lt;\/QueryPlan&gt;\r\n&lt;\/StmtSimple&gt;\r\n<\/pre>\n<p>Each <strong>&lt;RelOp&gt;<\/strong> element provides a complete break-down of the associated operation, including such details as the defined values, output list, estimated I\/O and CPU usage, average row size, and numerous other bits of information.<\/p>\n<p>From SSMS, you can save the XML to a file, just like you can any T-SQL code in SSMS, and use the file for further analysis, using whatever tools you have at your disposal.<\/p>\n<p>As a simple example, let\u2019s look at how this might work in T-SQL. Suppose we save the XML to the file <strong>execplan.xml<\/strong>. We can then use the <strong>OPENROWSET<\/strong> function to retrieve the XML from the file and insert it into a temporary table, as shown in the following example:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">CREATE TABLE #temp (ExecPlan XML);\r\n\r\nINSERT INTO #temp\r\nSELECT * FROM OPENROWSET(  \r\n   BULK 'c:\\datafiles\\showplans\\execplan.xml',  \r\n   SINGLE_BLOB) AS ExecPlan; \r\n<\/pre>\n<p>The table includes one column, <strong>ExecPlan<\/strong>, which is configured with the <strong>XML<\/strong> data type. We can now use the methods available to that data type to retrieve specific information from the XML. For example, the following <strong>SELECT<\/strong> statement uses the <strong>value<\/strong> method to return the statement\u2019s estimated number of rows:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT ExecPlan.value('declare namespace \r\n  ns=\"http:\/\/schemas.microsoft.com\/sqlserver\/2004\/07\/showplan\";\r\n  (\/ns:ShowPlanXML\/ns:BatchSequence\/ns:Batch\/ns:Statements\/ns:StmtSimple\/@StatementEstRows)[1]',\r\n  'nvarchar(20)') AS EstRows\r\nFROM #temp;\r\n<\/pre>\n<p>In this case, the statement returns <strong>7532.55<\/strong>, but we can just as easily return other types of information. For example, we can retrieve the original query on which the plan is based:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT ExecPlan.value('declare namespace \r\n  ns=\"http:\/\/schemas.microsoft.com\/sqlserver\/2004\/07\/showplan\";\r\n  (\/ns:ShowPlanXML\/ns:BatchSequence\/ns:Batch\/ns:Statements\/ns:StmtSimple\/@StatementText)[1]',\r\n  'nvarchar(max)') AS EstRows\r\nFROM #temp;\r\n<\/pre>\n<p>You can, of course, dig into the XML a lot deeper than what we\u2019ve looked at here and peel apart the individual operations to whatever degree is necessary. You can even parse multiple XML documents in order to compare execution plans from a set of queries or to compare the same query against different SQL Server instances. The XML makes it easy to persist and port execution plans across different environments, opening up a wide range of possibilities for query plan analysis.<\/p>\n<p>If you want to learn more about working with the XML query plans, take a look at Dennes Torres\u2019 article <a href=\"https:\/\/www.simple-talk.com\/sql\/t-sql-programming\/checking-the-plan-cache-warnings-for-a-sql-server-database\/\">Checking the Plan Cache Warnings for a SQL Server Database<\/a>.<\/p>\n<h2>Using SET statements to access execution plans<\/h2>\n<p>SQL Server also supports the ability to use <strong>SET<\/strong> statements to retrieve a query\u2019s execution plan. Each statement requires that you set the specified option to <strong>ON<\/strong> before capturing one or more plans, and then setting the option to <strong>OFF<\/strong> when you\u2019re finished.<\/p>\n<p>The two options we\u2019re most concerned with are <strong>SHOWPLAN_XML<\/strong> and <strong>STATISTICS<\/strong> <strong>XML<\/strong>. The <strong>SHOWPLAN_XML<\/strong> option returns the estimated plan as XML, without running the query. The <strong>STATISTICS<\/strong> <strong>XML<\/strong> option returns the actual plan, which means it must first run the query.<\/p>\n<p>SQL Server supports several other options related to generating execution plans, such as <strong>SHOWPLAN_TEXT<\/strong> or <strong>STATISTICS PROFILE<\/strong>, but those are slated for deprecation, so we\u2019ll give them a pass here.<\/p>\n<p>Let\u2019s start with the <strong>SHOWPLAN_XML<\/strong> option. To demonstrate how it works, we\u2019ll use the same <strong>SELECT<\/strong> statement we used for the previous examples:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SET SHOWPLAN_XML ON;\r\nGO\r\n\r\nSELECT sh.SalesOrderID, sh.CustomerID, sh.OrderDate, \r\n  sd.ProductID, sd.LineTotal\r\nFROM Sales.SalesOrderHeader sh JOIN Sales.SalesOrderDetail sd \r\n  ON sh.SalesOrderID = sd.SalesOrderID \r\nWHERE sd.LineTotal BETWEEN 1000 AND 1500;\r\nGO\r\n\r\nSET SHOWPLAN_XML OFF;\r\nGO\r\n<\/pre>\n<p>As you can see, we set the option to <strong>ON<\/strong>, specify the <strong>SELECT<\/strong> statement, and then set the option to <strong>OFF<\/strong>. If we did not set it to <strong>OFF<\/strong>, the database engine will continue to return the execution plan for each statement we run within the current session, without returning any query results.<\/p>\n<p>When you run a query with the <strong>SHOWPLAN_XML<\/strong> option is set to <strong>ON<\/strong>, the database engine returns a single value that contains the plan\u2019s XML. You can copy the value and paste it into a text editor, but when I tried it in a couple different editors, everything came over on a single line. However, you can also get at the XML in SSMS by clicking the value, which serves as a link that opens the graphical version of execution plan, just like the graphical plan we saw in our earlier examples. This means you can get the XML by right-clicking the graphical execution plan and then clicking <strong>Show<\/strong> <strong>Execution<\/strong> <strong>Plan<\/strong> <strong>XML<\/strong>.<\/p>\n<p>The <strong>STATISTICS<\/strong> <strong>XML<\/strong> option works much the same way as the <strong>SHOWPLAN_XML<\/strong> option, except that, as already noted, the database engine also executes the query, which makes it possible to get the actual execution plan. Here\u2019s what it looks like in action:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SET STATISTICS XML ON;\r\nGO\r\n\r\nSELECT sh.SalesOrderID, sh.CustomerID, sh.OrderDate, \r\n  sd.ProductID, sd.LineTotal\r\nFROM Sales.SalesOrderHeader sh JOIN Sales.SalesOrderDetail sd \r\n  ON sh.SalesOrderID = sd.SalesOrderID \r\nWHERE sd.LineTotal BETWEEN 1000 AND 1500;\r\nGO\r\n\r\nSET STATISTICS XML OFF;\r\nGO\r\n<\/pre>\n<p>Once again, click the results to view the graphical execution plan and subsequently access the XML, where you can save it to a file or do anything else with it that you like.<\/p>\n<h2>Using DMVs to access execution plans<\/h2>\n<p>Another method available for getting a query\u2019s execution plan is the <strong>sys.dm_exec_query_plan<\/strong> dynamic management view, which allows you to retrieve a cached execution plan or retrieve the plan for a currently running query.<\/p>\n<p>To use the view to retrieve an execution plan, you must provide the plan handle, an identifier that uniquely identifies the plan. Unfortunately, this can make using the view a bit tricky. Not many of us will know the plan handle off hand, which means we\u2019ll need to enlist one or more other views or methods to get the information we need, such as the <strong>sys.dm_exec_cached_plans<\/strong>, <strong>sys.dm_exec_query_stats<\/strong>, or <strong>sys.dm_exec_requests<\/strong> dynamic management views.<\/p>\n<p>For example, we can use the <strong>sys.dm_exec_query_plan<\/strong> view in conjunction with the <strong>sys.dm_exec_cached_plans<\/strong> and <strong>sys.dm_exec_sql_text<\/strong> dynamic management views to return a list of T-SQL statements and their query plans. This involves using the <strong>CROSS<\/strong> <strong>APPLY<\/strong> operator to provide the plan handles we need to return the execution plans.<\/p>\n<p>For example, suppose we run the same <strong>SELECT<\/strong> query we used in the previous examples and then run the following <strong>SELECT<\/strong> statement:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT st.text, qp.query_plan\r\nFROM sys.dm_exec_cached_plans cp\r\n  CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp\r\n  CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st\r\nWHERE st.dbid = 9\r\n<\/pre>\n<p>Notice that the <strong>WHERE<\/strong> clause limits the results to the database with the <strong>dbid<\/strong> value of <strong>9<\/strong>, which on my system is the <strong>AdventureWorks2014<\/strong> database. From the results, I can then locate the statement in the text column to come up with its associated plan.<\/p>\n<p>The plan itself is presented as an XML value much like the value returned by a <strong>SET<\/strong> statement, as we saw above. If you click the value, it opens the graphical plan in a separate tab, and from there you can access the XML.<\/p>\n<p>Not surprisingly, retrieving an execution plan in this way can get somewhat cumbersome, especially if there are a lot of cached plans. One option is to pass in the actually statement into the <strong>WHERE<\/strong> clause, as in the following example:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">SELECT qp.query_plan\r\nFROM sys.dm_exec_cached_plans cp\r\n  CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp\r\n  CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st\r\nWHERE st.dbid = 9 AND\r\n  st.text = 'SELECT sh.SalesOrderID, sh.CustomerID, sh.OrderDate, \r\n  sd.ProductID, sd.LineTotal\r\nFROM Sales.SalesOrderHeader sh JOIN Sales.SalesOrderDetail sd \r\n  ON sh.SalesOrderID = sd.SalesOrderID \r\nWHERE sd.LineTotal BETWEEN 1000 AND 1500;';\r\n<\/pre>\n<p>In this way, you return only a single result, just like <strong>SET<\/strong> <strong>SHOWPLAN_XML<\/strong> <strong>ON<\/strong>. However, you have to enter the T-SQL statement exactly how it is cached. For example, if you were to run the statement without including the trailing semi-colon, the outer <strong>SELECT<\/strong> statement would return no results.<\/p>\n<p>As you can see, the dynamic management view approach can get fairly cumbersome and is not particularly efficient. But it some cases, it might be your best option.<\/p>\n<h2>Using extended events to access execution plans<\/h2>\n<p>As an alternative to the other options we\u2019ve discussed, you can use SQL Server Extended Events to define events that capture execution plans. At one time, Microsoft recommended that you use SQL Server Profiler to capture execution plans, but Microsoft has since announced the deprecation of SQL Server Profiler for Trace Capture, and in its place, recommends that you use Extended Events.<\/p>\n<p>To use Extended Events to capture execution plans, you must first create a session that specifies exactly the type of plans you want to create. You can use the Extended Event GUI features in SSMS to create the session, or you can use a <strong>CREATE<\/strong> <strong>EVENT<\/strong> <strong>SESSION<\/strong> statement. In either case, you should specify the scope of the session and how to capture the execution plans.<\/p>\n<p>Let\u2019s look at a simple example of how to use a <strong>CREATE<\/strong> <strong>EVENT<\/strong> <strong>SESSION<\/strong> statement. (Be aware that there is a high overhead associated with this running this event, so proceed cautiously.) In this session, we\u2019ll specify the <strong>query_post_execution_showplan<\/strong> event, which will capture the actual execution plan for each query executed against the <strong>AdventureWorks2014<\/strong> database (with the ID of 9):<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true\">USE AdventureWorks2014;\r\nGO\r\n\r\nIF EXISTS(\r\n  SELECT * FROM sys.server_event_sessions WHERE name = 'ExecPlan')\r\nDROP EVENT SESSION ExecPlan ON SERVER;    \r\nGO\r\n\r\nCREATE EVENT SESSION ExecPlan ON SERVER \r\nADD EVENT sqlserver.query_post_execution_showplan(\r\n  ACTION(sqlserver.database_name)\r\n  WHERE ([source_database_id]=(9))) \r\nADD TARGET package0.event_file\r\n  (SET filename=N'C:\\DataFiles\\Showplans\\ExecPlan.xel')\r\nGO\r\n<\/pre>\n<p>The statement also specifies that the database name should be included in the results and that those results should be saved to the <strong>ExecPlan.xel<\/strong> file. There are, of course, numerous other options we can specify along with what we have here, such as capturing execution plans only if the query exceeds a certain amount of time. For a complete run-down of the <strong>CREATE<\/strong> <strong>EVENT<\/strong> <strong>SESSION<\/strong> statement, see the MSDN topic <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/bb677289.aspx\">CREATE EVENT SESSION (Transact-SQL)<\/a>.<\/p>\n<p>You can also use the SSMS interface to create the session. To get started, expand the <strong>Management<\/strong> tab in <strong>Object<\/strong> <strong>Explorer<\/strong>, and then expand the <strong>Extended<\/strong> <strong>Events<\/strong> node. Next, right-click the <strong>Sessions<\/strong> folder, and then click <strong>New<\/strong> <strong>Session<\/strong> or <strong>New<\/strong> <strong>Session<\/strong> <strong>Wizard<\/strong> to begin defining your session.<\/p>\n<p>After you create the session, you can use an <strong>ALTER<\/strong> <strong>EVENT<\/strong> <strong>SESSION<\/strong> statement to turn the session on, run a statement, and then use an <strong>ALTER<\/strong> <strong>EVENT<\/strong> <strong>SESSION<\/strong> statement turn the session off. The reason you want to explicitly turn off the session is that monitoring events in this way can be resource-intensive operations, so you want to use this process only when needed.<\/p>\n<p>The following T-SQL shows how you can use the Extended Events session to capture our query\u2019s execution plan:<\/p>\n<pre class=\"theme:ssms2012-simple-talk lang:tsql decode:true \">ALTER EVENT SESSION ExecPlan ON SERVER\r\nSTATE = START;\r\nGO\r\n\r\nSELECT sh.SalesOrderID, sh.CustomerID, sh.OrderDate, \r\n  sd.ProductID, sd.LineTotal\r\nFROM Sales.SalesOrderHeader sh JOIN Sales.SalesOrderDetail sd \r\n  ON sh.SalesOrderID = sd.SalesOrderID \r\nWHERE sd.LineTotal BETWEEN 1000 AND 1500;\r\nGO\r\n\r\nALTER EVENT SESSION ExecPlan ON SERVER\r\nSTATE = STOP;\r\nGO\r\n<\/pre>\n<p>When you turn on the session, SQL Server captures the event data to the specified file, adding a suffix to the file name to support generating multiple files as you turn the session on and off.<\/p>\n<p>After the file has been created, drag it to SSMS and click the applicable row in the upper pane to display details about the captured event in the lower pane, as shown in the following figure. In this case, there is only one row in the upper pane.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2016\/10\/word-image-13.png\" alt=\"%20screenshots\/stExecPlans_fig20.png\" \/><\/p>\n<p>In the bottom pane, you have two tabs. The <strong>Details<\/strong> tab includes various types of information about the query, and the <strong>Query<\/strong> <strong>Plan<\/strong> tab shows the graphical version of the plan, similar to what we saw in earlier examples, except that it does not include the top-level <strong>SELECT<\/strong> node.<\/p>\n<p>You can access the plan\u2019s XML from either tab. Accessing the XML from the <strong>Query<\/strong> <strong>Plan<\/strong> tab works just like we\u2019ve seen elsewhere. To access the XML from the <strong>Details<\/strong> tab, double-click the <strong>showplan_xml<\/strong> value. This opens the XML in a separate tab.<\/p>\n<h2>Mastering the execution plan<\/h2>\n<p>Microsoft provides plenty of options for getting at a query\u2019s estimated or actual execution plan. Regardless of the approach you take, you can view a graphical representation of the plan in SSMS or view the plan\u2019s XML in your tool of choice.<\/p>\n<p>Not surprisingly, there is much more to understanding and analyzing an execution plan than what we\u2019ve covered here, but getting that plan is always the first step, whether performed by you or someone else.<\/p>\n<p>The best way to learn about execution plans is to try out the various methods described here to generate plans for different types of queries and then playing around with the results. The more comfortable you are working with execution plans, the better you can analyze and fine-turn your queries, a topic we\u2019ll save for a separate discussion.<\/p>\n<h2>Further Reading<\/h2>\n<ol style=\"list-style: none;\">\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/performance\/execution-plan-basics\/\">Execution Plan Basics<\/a><\/li>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/performance\/why-developers-need-to-understand-execution-plans\/\">Why Developers Need to Understand Execution Plans<\/a><\/li>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/performance\/graphical-execution-plans-for-simple-sql-queries\/\">Graphical Execution Plans for Simple SQL Queries<\/a><\/li>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/database-administration\/the-sql-server-2016-query-store-forcing-execution-plans-using-the-query-store\/\">The SQL Server 2016 Query Store: Forcing Execution Plans using the Query Store<\/a><\/li>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/performance\/simple-query-tuning-with-statistics-io-and-execution-plans\/\">Simple Query tuning with STATISTICS IO and Execution plans<\/a><\/li>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/performance\/controlling-execution-plans-with-hints\/\">Controlling Execution Plans with Hints<\/a><\/li>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/database-administration\/exploring-query-plans-in-sql\/\">Exploring Query Plans in SQL<\/a><\/li>\n<li><a href=\"https:\/\/www.simple-talk.com\/sql\/t-sql-programming\/dmvs-for-query-plan-metadata\/\">DMVs for Query Plan Metadata<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Execution plans explain all you need to know  about query performance, and how to fine-tune.  Sure, you can see them in SSMS, but what if you need  to drill into to the important details? What about using DMVs, Extended Events or SET statements to get at the execution plans? To get the best use of execution plans you need to be able to get right information from the right plan at the right time. Robert Sheldon explains how.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143527,143531],"tags":[4783],"coauthors":[6779],"class_list":["post-68773","post","type-post","status-publish","format-standard","hentry","category-database-administration-sql-server","category-t-sql-programming-sql-server","tag-execution-plans"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/68773","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=68773"}],"version-history":[{"count":8,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/68773\/revisions"}],"predecessor-version":[{"id":71867,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/68773\/revisions\/71867"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=68773"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=68773"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=68773"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=68773"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}