<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQL in the Wild &#187; Execution Plans</title>
	<atom:link href="http://sqlinthewild.co.za/index.php/category/sql-server/execution-plans/feed/" rel="self" type="application/rss+xml" />
	<link>http://sqlinthewild.co.za</link>
	<description>A discussion on SQL Server</description>
	<lastBuildDate>Sun, 01 Jan 2012 14:30:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Compiles and recompiles</title>
		<link>http://sqlinthewild.co.za/index.php/2011/08/16/compiles-and-recompiles/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/08/16/compiles-and-recompiles/#comments</comments>
		<pubDate>Tue, 16 Aug 2011 14:30:00 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=1254</guid>
		<description><![CDATA[I want to spend some time over the next few months looking at query compilation and the plan cache, and there&#8217;s a couple concepts that I want to get cleared up beforehand. The first of those is around two terms that are often used interchangeably, compile and recompile. Compile A compile occurs when a query [...]]]></description>
			<content:encoded><![CDATA[<p>I want to spend some time over the next few months looking at query compilation and the plan cache, and there&#8217;s a couple concepts that I want to get cleared up beforehand. The first of those is around two terms that are often used interchangeably, compile and recompile.</p>
<h3>Compile</h3>
<p>A compile occurs when a query is given to the query optimiser and, when it does a lookup into the plan cache, no matching plan is found. The optimism must then compile the query, generating an execution plan, must add that plan to the plan cache (in most cases) and then must pass that plan onto the query execution engine so that the query can be executed. (<a href="http://technet.microsoft.com/en-us/library/Cc966425">http://technet.microsoft.com/en-us/library/Cc966425</a>)</p>
<h3>Recompile</h3>
<p>A recompile is something slightly different. For a recompile, the optimiser must find a matching plan when it queries the plan cache, must hand that cached plan over to the query execution engine and then while doing validation checks the execution engine must determine that then query plan is no longer valid and request the optimiser to partially or completely recompile the query. (<a href="http://technet.microsoft.com/en-us/library/Cc966425">http://technet.microsoft.com/en-us/library/Cc966425</a>)</p>
<p>Subtle difference. Both cases result in the optimiser generating an execution plan, but the reasons can be different. Also worth noting is that a compile results in a new plan in the cache, a recompile simply replaces an existing plan.</p>
<p>Another difference since SQL 2005 &#8211; a compile is for the entire batch, but a recompile can be for just a single statement within the batch.</p>
<hr />
<p>Now the theory&#8217;s dealt with, let&#8217;s look at some examples and see how we can track these two events and try and get a better understanding of which occurs when and how they look.</p>
<p>The tools I&#8217;m going to use to track these are performance monitor with the compiles/sec and recompiles/sec counters and SQL Profiler with the event SP:StmtRecompile event (there&#8217;s no profiler event for compilation). I&#8217;ll also check what&#8217;s in the plan cache after each test.</p>
<p>The first one’s going to be very simplistic, a query run against an empty plan cache.</p>
<pre class="brush: sql; title: ; notranslate">DBCC FREEPROCCACHE
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>What we get from that is a non-zero value for SQL Compilations/sec (perfmon) and the following from profiler (The SQL Recompiles/sec remains 0)</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles1.png"><img style="display: inline; border-width: 0px;" title="Compiles1" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles1_thumb.png" border="0" alt="Compiles1" width="484" height="52" /></a></p>
<p>and the plan cache now contains one plan with one use. (for more info on how the CacheMiss and CacheInsert events work, see <a href="http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/">http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/</a> and <a href="http://sqlinthewild.co.za/index.php/2010/08/31/come-and-gone/">http://sqlinthewild.co.za/index.php/2010/08/31/come-and-gone/</a>)</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles2.png"><img style="display: inline; border-width: 0px;" title="Compiles2" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles2_thumb.png" border="0" alt="Compiles2" width="484" height="24" /></a></p>
<p>In this case, I hope it was clear, we had a compile occur (empty plan cache before, new plan added to cache).</p>
<p>Now what happens if, with no clearing of the cache nor anything else being done, I mark that procedure for recompile and run it again?</p>
<p><span id="more-1254"></span></p>
<pre class="brush: sql; title: ; notranslate">EXEC sp_recompile OutStandingTotalByStatus
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>This is interesting. We don’t see a non-zero value for SQLRecompiles/sec (as might have been expected), instead, as with the previous case we get a spike for SQLCompiles/sec. The profiler output shows why</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles3.png"><img style="display: inline; border-width: 0px;" title="Compiles3" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles3_thumb.png" border="0" alt="Compiles3" width="484" height="122" /></a></p>
<p>Note the SP:CacheRemove being run for the stored procedure’s plan. So sp_recompile does not mark a plan as invalid and needing recompilation. It removes it from the cache entirely. The next time that procedure runs SQL does a cache lookup, doesn’t find a matching plan and compiles a new one (compile, not recompile)</p>
<p>Onwards… What about if I alter the procedure?</p>
<p>I’m not going to clear the cache first, the procedure has a plan in there from the last test, we can use that to see the effects.</p>
<pre class="brush: sql; title: ; notranslate">ALTER PROCEDURE OutStandingTotalByStatus
AS
-- This has changed!
SELECT 1;

SELECT o.OrderStatus, SUM(UnitPrice*Quantity) AS TotalOutstanding
FROM dbo.Orders o
INNER JOIN dbo.OrderDetails od ON o.OrderID = od.OrderID
WHERE ShippingDate IS NULL
GROUP BY OrderStatus;
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>Again perfmon shows a bump in Compiles/sec and the recompiles/sec is still at 0. Again, profiler shows why.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles4.png"><img style="display: inline; border-width: 0px;" title="Compiles4" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles4_thumb.png" border="0" alt="Compiles4" width="484" height="116" /></a></p>
<p>Same as when I ran the sp_recompile, the ALTER PROCEDURE triggered a SP:CacheRemove. The plan was gone from cache at the point the stored procedure ran again, so this was considered a compile, not a recompile.</p>
<p>btw, for those curious, if I query the plan cache between altering the procedure and running it again, the procedure’s plan really has gone.</p>
<p>Let’s try something more complex, an alteration of one of the base tables. I’m going to clear the cache first, for reasons that will later be clear</p>
<pre class="brush: sql; title: ; notranslate">DBCC FREEPROCCACHE
GO

EXEC dbo.OutStandingTotalByStatus
GO

ALTER TABLE dbo.OrderDetails ADD Filler CHAR(10)
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>Ha! This time perfmon shows a non-zero value for SQLRecompiles/sec. The profiler trace has a different form too</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles5.png"><img style="display: inline; border-width: 0px;" title="Compiles5" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles5_thumb.png" border="0" alt="Compiles5" width="484" height="92" /></a></p>
<p>There’s no SP:CacheRemove. No SP:CacheInsert either.</p>
<p>The alteration of the table did not result in SQL going through the plan cache and removing plans for dependant objects. That makes sense if you think about it. There could be millions of plans in cache, for SQL to check each and every one to see if it has a dependency on the table just changed would be terribly time consuming, so it’s not done. What happens instead is that the next time the procedure runs SQL looks in the cache, finds the procedure’s plan (the SP:CacheHit), does the pre-execution validations and finds that one of the base objects has changed since that plan was compiled. The plan is then sent to the optimiser to recompile that plan (the SP:Recompile event)</p>
<p>Here’s the other interesting thing, from a query of the plan cache</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles6.png"><img style="display: inline; border-width: 0px;" title="Compiles6" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles6_thumb.png" border="0" alt="Compiles6" width="484" height="38" /></a></p>
<p>The usecounts is 2, not 1. I ran that procedure once before altering the table, once after. The alter of the table forced SQL to recompile the plan on the second execution, but the usecount for the plan reflects both executions, the one from before the recompile and the one from after.</p>
<p>In all the previous cases, when the plan was actually removed from cache and then compiled and inserted the usecount was only 1 because when the plan was removed all related information was removed with it. With a recompile it’s not removed, so the usecounts aren’t reset to 0.</p>
<p>This is important to note. There are lots of articles (and books even) that say to check plan usecounts as a low usecount is a sign of frequent recompiles. As we can now see, it’s not. It’s a sign of frequent compiles, of the plans being removed from cache and later re-added, recompiles don’t affect the usecount (well, at least not in this case, there may be cases where it does)</p>
<p>Now try a couple more common cases, see what they show, starting with rebuilding an index.</p>
<pre class="brush: sql; title: ; notranslate">DBCC FREEPROCCACHE
GO

EXEC dbo.OutStandingTotalByStatus
GO

ALTER INDEX PK_OrderDetails ON dbo.OrderDetails REBUILD
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p>Any guesses?</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles7.png"><img style="display: inline; border-width: 0px;" title="Compiles7" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles7_thumb.png" border="0" alt="Compiles7" width="484" height="78" /></a></p>
<p>It’s a recompile (and the perfmon counters confirm that). As with the previous case, the usecount shown in sys.dm_exec_cached_plans is 2 for the procedure’s plan, not 1</p>
<p>Lastly, a statistics update. Can anyone guess what’s going to happen here? Anyone?</p>
<pre class="brush: sql; title: ; notranslate">DBCC FREEPROCCACHE
GO

EXEC dbo.OutStandingTotalByStatus
GO

-- Insert a few more rows into OrderDetails at this point

UPDATE STATISTICS dbo.OrderDetails WITH FULLSCAN
GO

EXEC dbo.OutStandingTotalByStatus
GO</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles9.png"><img style="display: inline; border-width: 0px;" title="Compiles9" src="http://sqlinthewild.co.za/wp-content/uploads/2011/08/Compiles9_thumb.png" border="0" alt="Compiles9" width="484" height="90" /></a></p>
<p>Yup, it’s a recompile.</p>
<p>It is worth nothing that (in my tests anyway), if at least one row isn’t changed, the stats update has no effect. Perhaps an optimisation that checks whether there have been modifications or not before updating? I’m not sure what’s causing that.</p>
<p>There is a lot more I could test, SET option changes, memory pressure, configuration changes, etc, but I think I’ve done enough that anyone interested can play themselves for further info.</p>
<p>Ok, but what’s the use? Was this just a dive into internals with no practical usage? I don’t think so.</p>
<p>One thing that’s important when investigating SQL problems is to understand what the various counters, events and columns are saying and what they mean. If one is faced with a server with very high SQLCompiles/sec, it’s a waste of time to go looking at whether automatic or manual stats updates are happening too often. Similarly if the SQLRecompiles/sec is high, investigating the usage of WITH RECOMPILE and OPTION(RECOMPILE) is completely off track and is going to waste time and not help with solving the problem.</p>
<p>That’s why I believe it’s important to understand what the various counters, events and columns actually means and what occurrences within SQL affects them. None of us have time to spend a couple days busy investigating something that’s not related to what’s actually wrong with the server based on an incorrect understanding of what SQL is showing us.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/08/16/compiles-and-recompiles/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Capturing the Execution Plan</title>
		<link>http://sqlinthewild.co.za/index.php/2011/01/04/capturing-the-execution-plan/</link>
		<comments>http://sqlinthewild.co.za/index.php/2011/01/04/capturing-the-execution-plan/#comments</comments>
		<pubDate>Tue, 04 Jan 2011 14:00:01 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=800</guid>
		<description><![CDATA[One last post on execution plans and Profiler (at least for now) When trying to check a query&#8217;s execution plan, the display execution plan option of Management Studio is usually adequate, however there are occasions where it&#8217;s either not feasible to run the query from Management Studio or the particular behaviour can&#8217;t be reproduced in [...]]]></description>
			<content:encoded><![CDATA[<p>One last post on execution plans and Profiler (at least for now)</p>
<p>When trying to check a query&#8217;s execution plan, the display execution plan option of Management Studio is usually adequate, however there are occasions where it&#8217;s either not feasible to run the query from Management Studio or the particular behaviour can&#8217;t be reproduced in Management Studio (perhaps because of different SET options). For cases like this it may be necessary to capture the execution plans via SQL Trace. Fortunately there are events for execution plans. Eight of them, to be precise, all under the Performance folder.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/PlanEvents.png"><img style="display: inline; border-width: 0px;" title="PlanEvents" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/PlanEvents_thumb.png" border="0" alt="PlanEvents" width="223" height="244" /></a></p>
<p>Great, so there&#8217;s no shortage of options available. But what are the differences between them?</p>
<h3>Showplan All</h3>
<p>According to Books Online:</p>
<blockquote><p>The <strong>Showplan All </strong>event class occurs when Microsoft SQL Server executes an SQL statement. Include this<strong> </strong>event class to identify the <strong>Showplan</strong> operators on Microsoft SQL Server 2000 or Microsoft SQL Server 7.0. This event class will also work on SQL Server 2005 and later; however, the information included is a subset of the information available in the <strong>Showplan XML Statistics Profile</strong> or <strong>Showplan XML </strong>event class.</p></blockquote>
<p>So in other words this one is not generally the event that we should be looking at on the latest versions of SQL. It&#8217;s not deprecated however (at least not in SQL 2008), so it is still usable on the later versions if you absolutely want.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/Showplan.png"><img style="display: inline; border-width: 0px;" title="Showplan" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/Showplan_thumb.png" border="0" alt="Showplan" width="484" height="76" /></a></p>
<p><span id="more-800"></span>The plan is in text format and hence it will be smaller than the XML format plan (like the one produced by Management Studio&#8217;s &#8220;include execution plan&#8221; option). Important to note is that the run-time statistics (eg &#8220;Actual row count&#8221;) are not present in this event. What is present is the text form of the plan, the estimated row counts for each operator, physical and logical operator names, any details of what the operator operated on, estimated rows, executions, IO and CPU costs, row size and some costs.</p>
<p>The event fires once per query executed, so if a single query is executed four times, the event will fire four times.</p>
<p>Also worth noting is that for all of the Showplan events, the BinaryData column must be included in the trace, as without that the plan will not be captured.</p>
<h3>Showplan All for Query Compile</h3>
<p>Very similar to the Showplan All event in appearance, the difference from the Showplan All event is when it fires. While Showplan All fires on query execution, Showplan All for Query Compile event fires when the query is optimised, not executed. Hence run the query four times and the Showplan All for Query Compile event will fire once, not four times (assuming that the plan is not already in cache and nothing forces a recompile on any of the executions)</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanQueryCompile.png"><img style="display: inline; border-width: 0px;" title="Showplan Query Compile" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanQueryCompile_thumb.png" border="0" alt="Showplan Query Compile" width="484" height="104" /></a></p>
<p>Like with the Showplan event, there is no run-time information. This shouldn&#8217;t be surprising, since the event fires at query compile, not execution.</p>
<h3>Showplan Statistics Profile</h3>
<p>Again, very similar to the Showplan All event in appearance. As with Showplan All event, the Books Online entry indicates that this is more use against SQL 7 and 2000 servers.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanQueryCompile1.png"><img style="display: inline; border-width: 0px;" title="Showplan Query Compile" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanQueryCompile_thumb1.png" border="0" alt="Showplan Query Compile" width="484" height="104" /></a></p>
<p>This is the first event that we&#8217;ve looked at that does contain the run-time information, specifically the execution count and row count for each operator.</p>
<h3>Showplan Text</h3>
<p>This is the event that produced the least information of all of the plan events. Books Online has a similar, but longer, note as for Showplan All.</p>
<blockquote><p>The <strong>Showplan Text </strong>event class occurs when Microsoft SQL Server executes an SQL statement. Include this event class to identify the Showplan operators on SQL Server 2000 or SQL Server 7.0. This event class works on SQL Server or later, however the information included is a subset of the information available in <strong>Showplan All</strong>, <strong>Showplan XML Statistics Profile</strong> or <strong>Showplan XML</strong> event class.</p></blockquote>
<p>All that this event shows is the text form of the plan. No estimated or actual statistics are included, no costs are available, no details of operator names or details.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanText.png"><img style="display: inline; border-width: 0px;" title="Showplan Text" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanText_thumb.png" border="0" alt="Showplan Text" width="484" height="98" /></a></p>
<p>This may be useful if there&#8217;s a need for a long-term trace for execution plans, since it&#8217;s the smallest amount of information it will result in the smallest files and lowest impact.</p>
<h3>Showplan Text (Unencoded)</h3>
<p>This event shows exactly the same information as Showplan Text does. The only difference is that the plan is formatted as a string and placed into the TextData column instead of the BinaryData column as with previous events.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanTextuuencode.png"><img style="display: inline; border-width: 0px;" title="Showplan Text uuencode" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanTextuuencode_thumb.png" border="0" alt="Showplan Text uuencode" width="484" height="104" /></a></p>
<h3>Showplan XML</h3>
<p>The XML plan events are the &#8216;new&#8217; events, first appearing in SQL 2005. One thing of interest with these events is that the resulting plans don&#8217;t have to be stored in the trace file, there is an option (with the profiler GUI that is) to save them into separate files. This is only an option when using the Profiler GUI, a server-side trace has no such capability.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/SeparateFiles.png"><img style="display: inline; border-width: 0px;" title="SeparateFiles" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/SeparateFiles_thumb.png" border="0" alt="SeparateFiles" width="364" height="234" /></a></p>
<p>With this event, the plan is stored in the textdata column, so the BinaryData column is not required, unlike with the Showplan All events.</p>
<p>Like with the Showplan All event, this event contains no run-time information. It has only the compile time information, much as the &#8216;Display Estimated Execution Plan&#8217; option in Management Studio.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanXML.png"><img style="display: inline; border-width: 0px;" title="Showplan XML" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanXML_thumb.png" border="0" alt="Showplan XML" width="484" height="116" /></a></p>
<p>The event fires once per query executed, so if a single query is executed four times, the event will fire four times.</p>
<h3>Showplan XML for Query Compile</h3>
<p>Almost identical to the Showplan XML event, the difference is that this event fires on query compile rather than query execution. Hence run the query four times and the Showplan XML for Query Compile event will fire once, not four times.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanXMLCompile.png"><img style="display: inline; border-width: 0px;" title="Showplan XML Compile" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanXMLCompile_thumb.png" border="0" alt="Showplan XML Compile" width="484" height="146" /></a></p>
<h3>Showplan XML Statistics Profile</h3>
<p>This is pretty much the top of the execution plan events &#8211; the full XML execution plan, with all compile- and run-time information on each execution of the query.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanXMLStatistics.png"><img style="display: inline; border-width: 0px;" title="Showplan XML Statistics" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/ShowplanXMLStatistics_thumb.png" border="0" alt="Showplan XML Statistics" width="484" height="314" /></a></p>
<p>Of course, as the largest of the plan events it&#8217;s going to have an impact on the server and as such should be traced with caution. It&#8217;s also going to result in large trace files if run for any length of time. Test first before running a trace against a busy production server.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2011/01/04/capturing-the-execution-plan/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Do IF statements cause recompiles?</title>
		<link>http://sqlinthewild.co.za/index.php/2010/12/14/do-if-statements-cause-recompiles/</link>
		<comments>http://sqlinthewild.co.za/index.php/2010/12/14/do-if-statements-cause-recompiles/#comments</comments>
		<pubDate>Tue, 14 Dec 2010 14:30:54 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=775</guid>
		<description><![CDATA[I heard this one over at SSC a while back. “Avoid IF statements in stored procedures as they result in recompiles” Ok, it sounds reasonable, if the optimiser optimises for the execution path taken on first execution it’ll have to go back and optimise the other paths when they are executed. But is that really [...]]]></description>
			<content:encoded><![CDATA[<p>I heard this one over at SSC a while back. “Avoid IF statements in stored procedures as they result in recompiles”</p>
<p>Ok, it sounds reasonable, if the optimiser optimises for the execution path taken on first execution it’ll have to go back and optimise the other paths when they are executed.</p>
<p>But is that really how it works? Now that I’ve spend some time looking at how the cache monitoring trace events behave, it’s possible to find out using those.</p>
<p>Let’s start with a simple example</p>
<pre class="brush: sql; title: ; notranslate">CREATE TABLE Tbl1 (
  ID INT
);

CREATE TABLE Tbl2 (
  ID VARCHAR(10),
  SomeDate DATETIME
);
GO

CREATE PROCEDURE TestingRecompiles (@SomeParam INT)
AS
IF (@SomeParam = 1)
  SELECT ID FROM Tbl1;
ELSE
  SELECT SomeDate FROM Tbl2;
GO</pre>
<p>Simple enough. First execution will be with the parameter value of 1. I’m going to use Profiler to see what’s happening. Events traced are SP:CacheInsert, T-SQL:StmtRecompile and the XML Plan for query compile, so I can see exactly what plan was generated. I’m using the ‘For Query Compile’ event so that I can catch the plan at optimisation time, not at execution time.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/TraceEvents.png"><img style="display: inline; border-width: 0px;" title="TraceEvents" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/TraceEvents_thumb.png" border="0" alt="TraceEvents" width="484" height="128" /></a></p>
<p><span id="more-775"></span></p>
<p>With that trace running, I’m going to run the proc once with a value of 1 for the parameter. Important to note is that there is no cached plan for the procedure at this point.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/FirstExecution.png"><img style="display: inline; border-width: 0px;" title="FirstExecution" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/FirstExecution_thumb.png" border="0" alt="FirstExecution" width="484" height="50" /></a></p>
<p>I have two plan for query compile events, one cache insert for the procedure and a batch completed. I want to focus on the two compile events.</p>
<p>The first one shows a table scan on tbl1. The second shows another table scan, on tbl2. So it appears that both branches of the IF statement went through the optimiser and had plans created for them.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/Plan1.png"><img style="display: inline; border-width: 0px;" title="Plan1" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/Plan1_thumb.png" border="0" alt="Plan1" width="484" height="124" /></a> <a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/Plan2.png"><img style="display: inline; border-width: 0px;" title="Plan2" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/Plan2_thumb.png" border="0" alt="Plan2" width="484" height="124" /></a></p>
<p>Before jumping to any conclusions, I want to see what&#8217;s sitting in the plan cache for this procedure.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/PlanWithIfs.png"><img style="display: inline; border-width: 0px;" title="PlanWithIfs" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/PlanWithIfs_thumb.png" border="0" alt="PlanWithIfs" width="484" height="164" /></a></p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/PlanWithIfs2.png"><img style="display: inline; border-width: 0px;" title="PlanWithIfs2" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/PlanWithIfs2_thumb.png" border="0" alt="PlanWithIfs2" width="364" height="163" /></a></p>
<p>One plan for that procedure and the plan shows a conditional (the if) and query operators for both branches.</p>
<p>So far it looks like the plan that was generated on the first execution contains plans for all of the statements and is sufficient for any execution, but to be sure, let&#8217;s see what happens when I run the query again with a different parameter.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/NoRecompile.png"><img style="display: inline; border-width: 0px;" title="NoRecompile" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/NoRecompile_thumb.png" border="0" alt="NoRecompile" width="484" height="58" /></a></p>
<p>No query compile, no cache insert, no recompile statement. So in this case there was no recompile from the IF.</p>
<p>Before drawing any conclusions, I want to check a more complex procedure, as the plans here were Trivial. Maybe full optimisation will show a different result.</p>
<pre class="brush: sql; title: ; notranslate">USE Adventureworks
GO

CREATE PROCEDURE TestingRecompiles2 (@SomeParam INT)
AS
IF (@SomeParam = 1)
  SELECT p.[Title], p.[FirstName], p.[MiddleName], p.[LastName]
    FROM [HumanResources].[Employee] e
      INNER JOIN [Person].[Person] p ON p.[BusinessEntityID] = e.[BusinessEntityID]
      INNER JOIN [Person].[BusinessEntityAddress] bea ON bea.[BusinessEntityID] = e.[BusinessEntityID]
      INNER JOIN [Person].[Address] a ON a.[AddressID] = bea.[AddressID]
ELSE
  SELECT s.[Name], a.[AddressLine1],a.[AddressLine2],a.[City],a.[PostalCode]
    FROM [Sales].[Store] s
      INNER JOIN [Person].[BusinessEntityAddress] bea ON bea.[BusinessEntityID] = s.[BusinessEntityID]
      INNER JOIN [Person].[Address] a ON a.[AddressID] = bea.[AddressID]
GO</pre>
<p>These queries are complex enough to go through full optimisation, but the profiler trace shows the same as before</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/MoreComplex.png"><img style="display: inline; border-width: 0px;" title="MoreComplex" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/MoreComplex_thumb.png" border="0" alt="MoreComplex" width="484" height="102" /></a></p>
<p>Two showplan events prior to the completion of the first execution, one for each branch of the IF, one cache insert event and no showplan events, no recompile events when the second execution (with a different parameter value) began. (I&#8217;ll leave examining the plan in cache as an exercise to the reader)</p>
<p>One more, just to be thorough.</p>
<pre class="brush: sql; title: ; notranslate">CREATE PROCEDURE TestingRecompiles3
AS
IF EXISTS (SELECT 1 FROM tbl1 WHERE ID&gt;0)
 SELECT ID FROM tbl1
ELSE
 SELECT ID FROM tbl2
GO

INSERT INTO tbl1 (ID) -- Pre-populate with 1 row so that stats-based recompiles don't confuse the issue.
 VALUES (0)
GO

EXEC TestingRecompiles3
GO

INSERT INTO tbl1 (ID)
VALUES (1)
GO

EXEC TestingRecompiles3
GO</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/12/NoRecompile3.png"><img class="alignnone size-full wp-image-777" title="NoRecompile3" src="http://sqlinthewild.co.za/wp-content/uploads/2010/12/NoRecompile3.png" alt="" width="482" height="112" /></a></p>
<p>Much the same as we saw in the earlier tests. This time there are three showplan events, one for the subquery in the IF and one for each branch. Again there is one CacheInsert for the entire procedure and no recompile events on the second execution</p>
<p>So what is actually happening?</p>
<p>When the optimiser receives a batch or object to generate execution plans for it does not execute any portion of that code. That&#8217;s not its job, that&#8217;s what the Query Execution engine is there for. The optimiser&#8217;s job is to optimise each statement within the procedure or batch<sup>(1)</sup>, to generate execution plans that are good enough and, in most cases, are reusable.</p>
<p>When it comes across an IF statement, the optimiser is not going to execute the expression to see which branch will be taken on this execution<sup>(2)</sup>. It&#8217;s going to optimise both branches because it has no idea which one will be executed on this or subsequent executions.</p>
<p>For each statement within the procedure, the optimiser generates the best plan it can find based on the parameter values for the current execution. This is not necessarily ideal as it can end up optimising queries for parameter values that they will never be called with. As can be imagined, this can generate some not-so-optimal execution plans. I <a href="http://sqlinthewild.co.za/index.php/2009/09/15/multiple-execution-paths/">wrote about this problem some time back</a>, so I&#8217;m not going to touch on it again</p>
<p>So, in conclusion… Do IF statements cause recompiles? No. The optimiser processes all branches of a conditional no matter which branch will be taken during execution, even if the branch can never be executed. Use of IF statements can however result in some really awful plans and make you sincerely wish they had caused a recompile. Use with caution (or recompile hints) if plans can be radically different depending on parameter values on the first execution.</p>
<p>(1) There are cases where the optimiser won&#8217;t optimise a statement within a procedure during the initial optimisation and will leave the statement to be optimised later (called deferred compile), but this is not due to IF statements. The one thing that immediately comes to mind that does cause this behaviour is when the statement depends on an object that does not exist at the point that the first optimisation is done, such as when a table is created within the procedure and then used within the same procedure.</p>
<p>(2) As proof that the optimiser doesn&#8217;t evaluate conditional expressions, consider the following:</p>
<pre class="brush: sql; title: ; notranslate">IF (42/0 = 137)
  SELECT name, sys.objects.type_desc FROM sys.objects</pre>
<p>If that is executed, it fails with a divide by zero error (as expected). Requesting the estimated execution plan succeeds and returns a valid execution plan and executing it with the profiler trace that I used above (adding in the user error event) shows the Showplan for query compile (as the select within the IF is optimised), then a cache insert for that plan, and then only is the divide by zero error thrown, just before the SQL:BatchCompleted event.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2010/12/14/do-if-statements-cause-recompiles/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Recompiles</title>
		<link>http://sqlinthewild.co.za/index.php/2010/11/18/recompiles/</link>
		<comments>http://sqlinthewild.co.za/index.php/2010/11/18/recompiles/#comments</comments>
		<pubDate>Thu, 18 Nov 2010 14:30:29 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=747</guid>
		<description><![CDATA[I&#8217;ve looked at cache hit and miss events and at the cache insert and remove events. The only cache-monitoring event (of current interest) left is the recompile event. There are two recompile events available in Profiler in SQL 2008 SP:Recompile under Stored Procedures SQL:StmtRecompile under T-SQL Which to use when? Books Online has the following [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve looked at <a href="http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/">cache hit and miss</a> events and at the <a href="http://sqlinthewild.co.za/index.php/2010/08/31/come-and-gone/">cache insert and remove</a> events. The only cache-monitoring event (of current interest) left is the recompile event.</p>
<p>There are two recompile events available in Profiler in SQL 2008</p>
<ul>
<li>SP:Recompile under Stored Procedures</li>
<li>SQL:StmtRecompile under T-SQL</li>
</ul>
<p>Which to use when?</p>
<p>Books Online has the following to say on the two events.</p>
<blockquote><p>The <strong><a href="http://msdn.microsoft.com/en-us/library/ms179294%28v=SQL.100%29.aspx">SQL:StmtRecompile</a></strong> event class indicates statement-level recompilations caused by all types of batches: stored procedures, triggers, ad hoc batches, and queries. Starting in SQL Server 2005, the <strong>SQL:StmtRecompile</strong> event class should be used instead of the <strong>SP:Recompile</strong> event class.</p></blockquote>
<p>and</p>
<blockquote><p>The <strong><a href="http://msdn.microsoft.com/en-us/library/ms187105%28v=SQL.100%29.aspx">SP:Recompile</a></strong> event class indicates that a stored procedure, trigger, or user-defined function has been recompiled. In SQL Server 2005 and later, recompilations reported by this event class occur at the statement level, whereas those in SQL Server 2000 occurred at the batch level.</p>
<p>In SQL Server 2005 and later, the preferred way to trace statement-level recompilations is to use the <strong>SQL:StmtRecompile</strong> event class. Starting in SQL Server 2005, the <strong>SP:Recompile</strong> event class is deprecated.</p></blockquote>
<p>So it appears that they show the same thing and SP:Recompile is deprecated. That simplifies the entire situation, the only one that I&#8217;m going to look at in that case is the SQL:StmtRecompile event.</p>
<p>So what does the event look like?</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RecompileEvents.png"><img style="display: inline; border-width: 0px;" title="RecompileEvents" src="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RecompileEvents_thumb.png" border="0" alt="RecompileEvents" width="496" height="82" /></a></p>
<p><span id="more-747"></span></p>
<p>Not too different from the other cache events. One difference is that the ad-hoc SQL statement no longer has an Object ID, only the stored procedure has an Object ID and it matches (as one would expect) with the Object ID in the system tables.</p>
<p>Another difference is that if the statement being recompiled is part of a procedure, the stored procedure name does not appear in the TextData column, rather the statement does. The way to tell whether the recompile was ad-hoc code or part of a procedure is to look at the ObjectID and ObjectName columns, which are only populated if the recompiled statement was part of a procedure (or trigger or function, etc)</p>
<p>One column that&#8217;s important here is the EventSubClass. The different values of this column, nicely interpreted by Profiler, give the reason that the recompile was necessary.</p>
<p>As per Books Online, the EventSubClass can have the following values.</p>
<ol>
<li>Schema changed</li>
<li>Statistics changed</li>
<li>Deferred compile</li>
<li>Set option changed</li>
<li>Temp table changed</li>
<li>Remote rowset changed</li>
<li>For Browse permissions changed</li>
<li>Query notification environment changed</li>
<li>Partition view changed</li>
<li>Cursor options changed</li>
<li>Option (recompile) requested</li>
</ol>
<p>Some of those are not occurrences that will be encountered all that often, and I have found at least one that&#8217;s not on that list. (I remember seeing a &#8217;12&#8242; in the subclass column once, but can&#8217;t recall what the description was)</p>
<p>So, now that we&#8217;ve seen what the event looks like, let&#8217;s see when it fires. The event subclass gives a good list of the causes, I&#8217;m not going to go over all of the subclasses, just some of the more common (and easier to demo). I won&#8217;t guarantee that what follows is a comprehensive list of everything that causes the various recompiles, I&#8217;m sure to miss something.</p>
<p>Using the same example table as previous cache-investigation posts did.</p>
<h3>Schema Changes</h3>
<p>This recompile occurs when any of the base tables for the query have changed, when any of the indexes for the query have been rebuild or changed and when sp_recompile is run for any of the base tables.</p>
<p>It does not occur for a procedure if that procedure is altered, nor does it appear if sp_recompile is run for the procedure. Both of those result in a sp:CacheRemove event.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RecomplieSchemaChange.png"><img style="display: inline; border-width: 0px;" title="RecomplieSchemaChange" src="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RecomplieSchemaChange_thumb.png" border="0" alt="RecomplieSchemaChange" width="496" height="184" /></a></p>
<h3>Statistics Changed</h3>
<p>Fairly obvious. The recompile occurred because statistics that the statement in question used have changed.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RecompileStatsChange.png"><img style="display: inline; border-width: 0px;" title="RecompileStatsChange" src="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RecompileStatsChange_thumb.png" border="0" alt="RecompileStatsChange" width="501" height="68" /></a></p>
<p>The update of some rows is necessary to get the recompile. If the stats are updated and there were no data modifications made since the previous update stats, the recompile does not occur.</p>
<p>This recompile also occurs when an autoupdate of the stats that the query uses has occurred.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RecompileStatsChange2.png"><img style="display: inline; border-width: 0px;" title="RecompileStatsChange2" src="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RecompileStatsChange2_thumb.png" border="0" alt="RecompileStatsChange2" width="494" height="86" /></a></p>
<h3>Deferred compile</h3>
<p>A deferred compile occurs when an object referenced by a statement within the batch does not exist when the batch is first compiled.</p>
<pre class="brush: sql; title: ; notranslate">CREATE PROCEDURE [dbo].[TestingCacheEvents2]
AS

SELECT ID, SomeDate, Status
FROM TestingCacheEvents
WHERE Status = 'G'

CREATE TABLE #Temp (Status char(2), CountStatus int)

INSERT INTO #Temp
SELECT status, count(*)
FROM TestingCacheEvents
GROUP BY Status

DROP TABLE #Temp</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/11/DeferredCompile1.png"><img style="display: inline; border-width: 0px;" title="DeferredCompile1" src="http://sqlinthewild.co.za/wp-content/uploads/2010/11/DeferredCompile1_thumb.png" border="0" alt="DeferredCompile1" width="499" height="51" /></a></p>
<p>Now in SQL 2000 and earlier, this form of procedure would have recompiled (entirely) on every execution due to the creation of the temp table part way through. If the procedure ran frequently, this could have a devastating effect on performance. In SQL 2005 and above however this does not happen. As can be seen in the following screenshot, the second execution does not incur a recompile.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/11/DeferredCompile2.png"><img style="display: inline; border-width: 0px;" title="DeferredCompile2" src="http://sqlinthewild.co.za/wp-content/uploads/2010/11/DeferredCompile2_thumb.png" border="0" alt="DeferredCompile2" width="494" height="71" /></a></p>
<h3>Set option changed</h3>
<p>This one occurs when a set option is changed within the procedure, not outside the procedure.</p>
<pre class="brush: sql; title: ; notranslate">CREATE PROCEDURE [dbo].[TestingCacheEvents3]
AS

SELECT ID, SomeDate, Status
FROM TestingCacheEvents
WHERE Status = 'G'

SET ARITHABORT OFF

SELECT DATEDIFF(dd,SomeDate,GETDATE())
FROM TestingCacheEvents
WHERE Status = 'G'</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/11/SetOptionsChanged.png"><img style="display: inline; border-width: 0px;" title="SetOptionsChanged" src="http://sqlinthewild.co.za/wp-content/uploads/2010/11/SetOptionsChanged_thumb.png" border="0" alt="SetOptionsChanged" width="497" height="53" /></a></p>
<h3>Option Recompile requested</h3>
<p>If this one isn&#8217;t obvious, then I don&#8217;t know what could be.</p>
<pre class="brush: sql; title: ; notranslate">CREATE PROCEDURE [dbo].[TestingCacheEvents5]
AS

SELECT ID, SomeDate, Status
FROM TestingCacheEvents
WHERE Status = 'G'

SELECT Status, COUNT(*)
FROM TestingCacheEvents
GROUP BY Status
OPTION (RECOMPILE)</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RequestedRecompile.png"><img style="display: inline; border-width: 0px;" title="RequestedRecompile" src="http://sqlinthewild.co.za/wp-content/uploads/2010/11/RequestedRecompile_thumb.png" border="0" alt="RequestedRecompile" width="496" height="59" /></a></p>
<hr />That covers all the cache-monitoring events that I want to look at. Now I can get onto some fun stuff that uses this.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2010/11/18/recompiles/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Come and gone</title>
		<link>http://sqlinthewild.co.za/index.php/2010/08/31/come-and-gone/</link>
		<comments>http://sqlinthewild.co.za/index.php/2010/08/31/come-and-gone/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 14:00:59 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=643</guid>
		<description><![CDATA[Or &#8220;Plan cache monitoring &#8211; insert and remove&#8221; Previously I took a look at the CacheHit and CacheMiss events to see how they behave and how to identify what&#8217;s been searched for in the cache. in this follow up, I want to take a similar look at the CacheInsert and CacheRemove events, see when they [...]]]></description>
			<content:encoded><![CDATA[<p>Or &#8220;<em>Plan cache monitoring &#8211; insert and remove</em>&#8221;</p>
<p>Previously I took a look at the <a href="http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/">CacheHit and CacheMiss</a> events to see how they behave and how to identify what&#8217;s been searched for in the cache. in this follow up, I want to take a similar look at the CacheInsert and CacheRemove events, see when they fire and how to identify the objects that they relate to.</p>
<p>Again, a word of caution, these can be frequently occurring events on busy servers and so traces should be kept short and to a minimum of events and columns. That said, these should occur a lot less often than the CacheHit and CacheMiss events. If they are occurring very frequently it may indicate that the SQL Server is not reusing plans efficiently.</p>
<h3>CacheInsert</h3>
<p>The CacheInsert event fires after a CacheMiss. The search for a matching plan in the cache failed, firing a CacheMiss event. Since there&#8217;s no plan, the optimiser is invoked to generate one and then that plan is inserted into the plan cache before the Query Execution engine begins execution.</p>
<p>The event is fairly simple, though of course there are a few surprises (what in SQL doesn&#8217;t have?).</p>
<pre class="brush: sql; title: ; notranslate">Exec FireCacheEvents
GO

SELECT ID, SomeDate, Status
FROM TestingCacheEvents
WHERE Status =  'C'</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/08/CacheInsert.png"><img style="display: inline; border-width: 0px;" title="CacheInsert" src="http://sqlinthewild.co.za/wp-content/uploads/2010/08/CacheInsert_thumb.png" border="0" alt="CacheInsert" width="490" height="83" /></a></p>
<p><span id="more-643"></span></p>
<p>Three CacheInsert events for two batches. The first is simple enough, it&#8217;s the insert of the plan for the stored procedure. The procedure name is in the TextData column and the procedure&#8217;s ID is in the ObjectID column. The ObjectName column is not populated for this event.</p>
<p>The second and third Cacheinsert events are the interesting ones. The second one shows a parameterised version of the ad-hoc SQL statement, while the third shows an unparameterised version of the same ad-hoc SQL statement. Clearly this query was simple enough to qualify for auto-parameterisation. So, are there two plans for this been inserted into cache? To answer that one, I need to switch over to SSMS and query the plan cache.</p>
<pre class="brush: sql; title: ; notranslate">select usecounts, size_in_bytes, cacheobjtype, objtype, st.text,  qp.query_plan
 from sys.dm_exec_cached_plans cp
 cross apply  sys.dm_exec_sql_text(cp.plan_handle) st
 cross apply  sys.dm_exec_query_plan(cp.plan_handle) qp
 where st.text not like  '%sys.dm_exec%'</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/08/ExecCachedPlans.png"><img style="display: inline; border-width: 0px;" title="ExecCachedPlans" src="http://sqlinthewild.co.za/wp-content/uploads/2010/08/ExecCachedPlans_thumb.png" border="0" alt="ExecCachedPlans" width="484" height="56" /></a></p>
<p>Well, there are three entries and the Cache Object Type is listed as compiled plan for all three, but there&#8217;s something different between them. Take a look at the size in bytes for the ad-hoc and prepared statements. The ad-hoc is less than half the size of the prepared. That&#8217;s not the only thing different. If I open up the execution plans, the exec plan for the prepared statement looks normal. There&#8217;s a clustered index scan (there are no nonclustered indexes on this table yet) and a select operator. The plan for the adhoc statement however…</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/08/NotaPlan.png"><img style="display: inline; border-width: 0px;" title="NotaPlan" src="http://sqlinthewild.co.za/wp-content/uploads/2010/08/NotaPlan_thumb.png" border="0" alt="NotaPlan" width="475" height="100" /></a></p>
<p>There&#8217;s something missing here, like the rest of the plan. Ok, so the entry with the ad-hoc type is not a complete plan, so what is it?</p>
<p>There doesn&#8217;t seem to be anything on MSDN on this (at least nothing I could find), but two of Kalen&#8217;s books<sup>1</sup>. (Inside Microsoft SQL Server 2005: Query Tuning and Optimisation, page 283; Microsoft SQL Server 2008 Internals, page 533) mention this. These ad-hoc entries are shell queries, cached just to make the parameterised form of the query easier to find. All the execution plan contains for these shell queries is a pointer to the plan for the parameterised version of the query.</p>
<p>For the ad-hoc query that gets inserted as a shell plan and the parameterised query, the objectID is, as with the CacheHit and CacheMiss events, a hash of the query text and does not match to any object in the database. The full text of the batch is then given in the TextData column.</p>
<p>I think that&#8217;s enough for CacheInsert. On to it&#8217;s opposite.</p>
<h3>CacheRemove</h3>
<p>The CacheRemove event fires, as one would expect, when a plan is removed from cache. The interesting question is, when does that occur?</p>
<p>Is it every event that could possibly result in an inefficient or invalid plan, or just memory pressure and explicit cache flushes that trigger the CacheRemove event?</p>
<p>Let&#8217;s see…</p>
<p>As an aside, I&#8217;m going to add a nonclustered index to the table that I&#8217;m using, so that the update of statistics will have an effect. Otherwise the only execution path possible is a table scan, and changing statistics won&#8217;t affect that.</p>
<pre class="brush: sql; title: ; notranslate">CREATE NONCLUSTERED INDEX idx_TestingCacheEvents_Status
 ON  TestingCacheEvents (Status)
GO</pre>
<p>Right, onto the testing.</p>
<pre class="brush: sql; title: ; notranslate">Exec FireCacheEvents
GO
SELECT ID, SomeDate, Status
FROM  TestingCacheEvents
WHERE Status = 'C'
GO
DBCC FREEPROCCACHE -- Gone.  All gone
WAITFOR DELAY '00:00:05'
GO

Exec FireCacheEvents
GO
SELECT ID, SomeDate, Status
FROM  TestingCacheEvents
WHERE Status = 'C'
GO
DBCC  FREEPROCCACHE(0x0500060063A9355540614285000000000000000000000000) -- Just one removed, the proc
WAITFOR DELAY '00:00:05'
GO

Exec FireCacheEvents
GO
SELECT ID, SomeDate, Status
FROM  TestingCacheEvents
WHERE Status = 'C'
GO
exec sp_recompile  'TestingCacheEvents' -- recompile on the table, both proc and ah-hoc plans  invalidated.
WAITFOR DELAY '00:00:05'
GO

Exec FireCacheEvents
GO
SELECT ID, SomeDate, Status
FROM  TestingCacheEvents
WHERE Status = 'C'
GO
UPDATE TOP(1)  TestingCacheEvents
 SET Status = 'C'
 WHERE Status = 'B'
UPDATE STATISTICS TestingCacheEvents WITH FULLSCAN
WAITFOR DELAY  '00:00:05'
GO

Exec FireCacheEvents
GO
SELECT ID, SomeDate, Status
FROM  TestingCacheEvents
WHERE Status = 'C'
GO
ALTER TABLE  TestingCacheEvents ALTER COLUMN FILLER CHAR(325)
WAITFOR DELAY '00:00:05'
GO
Exec FireCacheEvents
GO
SELECT ID, SomeDate, Status
FROM  TestingCacheEvents
WHERE Status = 'C'
GO</pre>
<p>Well, this is interesting…</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/08/CacheRemove.png"><img style="display: inline; border-width: 0px;" title="CacheRemove" src="http://sqlinthewild.co.za/wp-content/uploads/2010/08/CacheRemove_thumb.png" border="0" alt="CacheRemove" width="492" height="230" /></a></p>
<p>The only things that caused the CacheRemove event to fire were the two DBCC FreeProcCache, the first that, as can be seen from the EventSubClass, cleared the entire plan cache, and the second that cleared a single plan.</p>
<p>If I go back and look at the whitepaper <a href="http://msdn.microsoft.com/en-us/library/ee343986.aspx">Plan Caching in SQL Server 2008</a>, it differentiates between removing a plan from cache (which memory pressure and cache flushes do) and invalidating the plan (which recompiles, schema changes, stats changes and the like do). Based on that whitepaper and these results, I would conclude that the CacheRemove event only fires when the plan is actually removed from cache, not when it&#8217;s just invalidated. The invalidation of a plan simply results in a recompile next time the plan is needed.</p>
<p>Now that the question of when it appears is answered, let&#8217;s finish with what it looks like.</p>
<p>There are two possible formats for this event, and it&#8217;s the EventSubClass that shows which of the two the particular event is.</p>
<p>The first possibility is when all of the plan cache has been flushed, identified by a EventSubClass of &#8220;2 &#8211; ProcCacheFlush&#8221;. This will be a result of a DBCC FREEPROCCACHE and some server reconfiguration events. In this case, there will be no ObjectID and the text data will simply state that the cache has been flushed.</p>
<p>The second possibility is more interesting, especially if monitoring plans being aged out of memory. An EventSubClass of &#8220;1 &#8211; Compplan Remove&#8221; identifies a single plan being removed from cache. These are fired when either a single plan or part of the plan cache is cleared. Examples here are DBCC FREEPROCCACHE with a plan or SQL handle passed to it, DBCC FLUSHPROCINDB, database reconfigurations (restore, detach, offline)</p>
<p>With the one, the ObjectID does have a value and it&#8217;s the ObjectID of the pbject whose plan is being thrown out of cache. As with all the other cache events, for ad-hoc queries the ObjectID is just a hash of the query text and only really useful for matching to CacheMiss, CacheHit and CacheInsert events and the object name or ad-hoc batch appears in the TextData</p>
<p>That&#8217;s more than I planned to write, but CacheRemove turned out to be a little more complex than I expected. Next up, the last part of this sort series &#8211; the recompile events.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2010/08/31/come-and-gone/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Hit and miss</title>
		<link>http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/</link>
		<comments>http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/#comments</comments>
		<pubDate>Tue, 27 Jul 2010 14:00:10 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=377</guid>
		<description><![CDATA[Or &#8220;Monitoring plan cache usage&#8221; For people interested in the details of how SQL is using and reusing execution plans, there are some useful events in profiler for watching this in detail, under the Stored procedure group: SP:CacheMiss SP:CacheInsert SP:CacheHit SP:CacheRemove SP:Recompile SP:StmtRecompile Additionally there&#8217;s the SQL:StmtRecompile event under the TSQL group. For now, I [...]]]></description>
			<content:encoded><![CDATA[<p>Or &#8220;<em>Monitoring plan cache usage</em>&#8221;</p>
<p>For people interested in the details of how SQL is using and reusing execution plans, there are some useful events in profiler for watching this in detail, under the Stored procedure group:</p>
<ul>
<li>SP:CacheMiss</li>
<li>SP:CacheInsert</li>
<li>SP:CacheHit</li>
<li>SP:CacheRemove</li>
<li>SP:Recompile</li>
<li>SP:StmtRecompile</li>
</ul>
<p>Additionally there&#8217;s the SQL:StmtRecompile event under the TSQL group.</p>
<p>For now, I just want to look briefly at the CacheMiss and CacheHit events.</p>
<p>One word of caution early on, these are frequently occurring events and it may not be a good idea to trace these on busy production servers. If you do need to, keep the duration of the trace short and the columns to a minimum.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/07/CacheEvents.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="CacheEvents" src="http://sqlinthewild.co.za/wp-content/uploads/2010/07/CacheEvents_thumb.png" border="0" alt="CacheEvents" width="244" height="157" /></a></p>
<h3>CacheMiss</h3>
<p>The cache miss event fires any time SQL looks for the execution plans for an object or ad-hoc batch and does not find it in the plan cache.</p>
<p>For an object (scalar function, multi-statement table-valued function, stored procedure or trigger) the match is done on the object ID (along with some of the connection&#8217;s SET options and possibly the database user and c couple other factors<sup>1</sup>). For an ad-hoc batch, the match is done on a hash of the text of the batch (along with some of the connection&#8217;s SET options and possibly the database user)</p>
<p>When testing stored procedures from Management Studio (or another SQL querying tool), two CacheMiss events will appear in the trace.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/07/CacheMiss.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="CacheMiss" src="http://sqlinthewild.co.za/wp-content/uploads/2010/07/CacheMiss_thumb.png" border="0" alt="CacheMiss" width="490" height="87" /></a></p>
<p>What&#8217;s going on here?</p>
<p><span id="more-377"></span></p>
<p>Let&#8217;s start from the bottom and work up. The last event there, the SP:Completed records the completion of the stored procedure and lists both the ObjectID and ObjectName and, if I check in the database, ObjectID 1301579675 does indeed belong to the stored procedure FireCacheEvents.</p>
<p>The second event (the second cache miss) has the same ObjectID and ObjectName. So this is the failed cache lookup for the stored procedure (failed because this is the first time I ran it and the plan was, as a result, not in cache).</p>
<p>The first entry is the one that&#8217;s curious. The ObjectName is not populated and the ObjectID doesn&#8217;t match anything in the database. So what is the cache lookup trying to find?</p>
<p>The TextData column give a hint. What it&#8217;s trying to find is a cached plan for the ad-hoc batch containing the whole of the text submitted to SQL (In this case just &#8216;EXEC FireCacheEvents&#8217;). This too could contain queries (though it doesn&#8217;t in this case) and needs a plan lookup. With just an EXEC in the batch, it won&#8217;t have a plan and hence won&#8217;t be found in cache, but there&#8217;s still a lookup for it.</p>
<p>If there was any SELECT, INSERT, UPDATE or DELETE (or MERGE on SQL 2008) statement within that ad-hoc batch, the batch would also be cached and future cache lookups would succeed but, since it&#8217;s just an EXEC, there&#8217;s no plan to cache.</p>
<p>This additional cache lookup won&#8217;t occur if the execution is via RPC (e.g. A .Net call with the SQLCommand.CommandType = CommandType.StoredProcedure) but it will any time there is an ad-hoc SQL batch submitted (e.g. from Management Studio or .Net if the SQLCommand.CommandType is set to CommandType.Text)</p>
<p>That&#8217;s ad-hoc batches and stored procs. Let&#8217;s try the third possibility &#8211; a parameterised query.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/07/CacheMissparameterised.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="CacheMiss parameterised" src="http://sqlinthewild.co.za/wp-content/uploads/2010/07/CacheMissparameterised_thumb.png" border="0" alt="CacheMiss parameterised" width="496" height="61" /></a></p>
<p>This looks semi-familiar. The first CacheMiss is again for the entire of the ad-hoc batch (consisting of the DECLARE and the EXEC in this case). The second looks similar, something in TextData and no ObjectName. This is the parameterised query, identifiable as parameterised by the (@Status Char(1)) parameter definition right at the beginning. Again here the ObjectID is a hash of the text and doesn&#8217;t match to a real object in the database.</p>
<p>It is worth noting that, if both the CacheMiss and CacheInsert events are traced, the CacheMiss events will only appear if there is no subsequent CacheInsert for the same ObjectID. If I ran the exact same script as above, but had the SP:Completed, SP:CacheMiss and SP:CacheInsert events in the trace, there would still be only three events recorded, the cache miss for the ad-hoc batch (as that&#8217;s not cached there&#8217;s no matching CacheInsert event), the CacheInsert for the stored proc and then the SP:Completed. The CacheMiss for the procedure wouldn&#8217;t appear, though it&#8217;s presence can be intuited from the presence of the CacheInsert.</p>
<p>That should about cover it for the CacheMiss. If the failed cache lookup is for is an ad-hoc batch or parameterised query, the TextData column will be populated with the contents of the batch or query and the ObjectID will be a hash of the text (and shouldn&#8217;t match any object in the database). If the failed cache lookup is for a procedure (or function or trigger), the object name column is usually populated (not always) with the name of the object, TextData is blank and the ObjectID matches the ObjectID of the object in the database</p>
<h3>CacheHit</h3>
<p>Onto the CacheHit event. This, as its name implies, is the opposite to the CacheMiss. The CacheMiss indicates that a lookup to the plan cache failed to find a matching plan. The CacheHit indicates that a lookup to the plan cache succeeded in finding a matching plan (based on object id, set options, maybe user, and various other conditions).</p>
<p>It&#8217;s not certain, even if the cache lookup succeeds, that the plan will indeed be used for the execution of the query/batch/procedure as there are a number of stability and optimality related checks that will be done before the plan is used.</p>
<p>So let&#8217;s see how this event looks.</p>
<pre class="brush: sql; title: ; notranslate">EXEC FireCacheEvents
GO

SELECT ID, SomeDate, Status
FROM dbo.TestingCacheEvents
WHERE Status = 'B'
GO </pre>
<p>Two ad-hoc batches, first with just a stored procedure call, second with just an ad-hoc SQL statement.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2010/07/CacheHit.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="CacheHit" src="http://sqlinthewild.co.za/wp-content/uploads/2010/07/CacheHit_thumb.png" border="0" alt="CacheHit" width="492" height="85" /></a></p>
<p>No big surprises here, not after looking at the CacheMiss events. The first is the CacheMiss for the first of the ad-hoc batches, the one with just the EXEC in it. Since that is not cached, there will be a CacheMiss every time that executes.</p>
<p>The first of the CacheHit events is for the stored procedure. As with the CacheMiss, this CacheHit for the stored procedure has an ObjectID that matches the ObjectID for that procedure in the database, and the ObjectName column is populated with (surprise) the object name while the TextData is blank.</p>
<p>The second CacheHit is for the ad-hoc batch with the SELECT statement. As is probably expected by this point, the ObjectID there is just a hash of the text, the ObjectName is blank and the TextData is populated with the full text of the batch.</p>
<h3>In Conclusion</h3>
<p>If you&#8217;re monitoring cache usage with the CacheMiss and CacheHit events, there are two different ways to identify what the lookup was looking for.</p>
<p>If the lookup was for the plan of a stored procedure, trigger or function, the ObjectID column contains a value that matches the ObjectID for that object in the database. The TextData column is blank and the ObjectName column is (usually) populated with the name of the object. I did encounter a couple of cases where the ObjectName was blank for a CacheMiss event for a stored procedure, not quite sure why. More investigation is necessary.</p>
<p>If the lookup was for the plan of an ad-hoc batch or parameterised query, the ObjectID contains a meaningless value, the ObjectName column is blank and the TextData contains the entire of the batch/query.</p>
<p>It may also be worth mentioning that the DatabaseID column is populated for all CacheMiss and CacheHit events, regardless of what the lookup is looking for. Additionally the DatabaseName column is populated for all CacheHit events (but is not an available column for the CacheMiss)</p>
<p>(1) For anyone who wants more information, there are two excellent resources available from Microsoft:</p>
<ul>
<li><a href="http://technet.microsoft.com/en-gb/library/cc966425.aspx">Batch Compilation, Recompilation, and Plan Caching Issues in SQL Server 2005</a></li>
<li><a href="http://msdn.microsoft.com/en-us/library/ee343986.aspx">Plan Caching in SQL Server 2008</a></li>
</ul>
<p>Both go extremely deep into caching, what influences matching of plans, plan reuse, recompilations and the plan cache itself.</p>
<p>Reproduction code:</p>
<pre class="brush: sql; title: ; notranslate">CREATE TABLE TestingCacheEvents (
 ID INT IDENTITY PRIMARY KEY,
 SomeDate DATETIME,
 Status CHAR(1),
 Filler CHAR(300) DEFAULT ' '
);
GO

INSERT INTO TestingCacheEvents (SomeDate, Status)
SELECT TOP (10000)
 DATEADD(dd,FLOOR(RAND(a.object_id+b.column_id*5000)*500),'2000/01/01'),
 CHAR(65+FLOOR(RAND(b.object_id+a.column_id*5000)*10))
 FROM master.sys.columns a CROSS JOIN master.sys.columns b;
GO

CREATE PROCEDURE FireCacheEvents
AS
 SELECT ID, SomeDate, Status
 FROM TestingCacheEvents
 WHERE Status = 'G'

GO</pre>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2010/07/27/hit-and-miss/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Are trivial plans cached?</title>
		<link>http://sqlinthewild.co.za/index.php/2009/12/08/are-trivial-plans-cached/</link>
		<comments>http://sqlinthewild.co.za/index.php/2009/12/08/are-trivial-plans-cached/#comments</comments>
		<pubDate>Tue, 08 Dec 2009 15:00:28 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=495</guid>
		<description><![CDATA[It is sometimes said that trivial execution plans are not cached and queries that have such plans are compiled on every execution. So is that true? To effectively answer this question, we must first establish what a trivial plan is. A trivial plan is essentially a plan for a query where a specific plan will [...]]]></description>
			<content:encoded><![CDATA[<p>It is sometimes said that trivial execution plans are not cached and queries that have such plans are compiled on every execution. So is that true? To effectively answer this question, we must first establish what a trivial plan is.</p>
<p>A trivial plan is essentially a plan for a query where a specific plan will always be the most optimal way of executing it. If we consider something like SELECT * FROM SomeTable then there&#8217;s only one real way to execute it, a scan of the cluster/heap.</p>
<p>The trivial plan is somewhat of a query optimiser optimisation. If the query qualifies for a trivial plan (and there are lots of restrictions) then the full optimisation process doesn&#8217;t need to be started and so the query&#8217;s execution plan can be generated quicker and with less overhead. The fact that a query has a trivial plan at one point doesn’t necessarily mean that it will always have a trivial plan, indexes may be added that make the selection of plan less of a sure thing and so the query must go for full optimisation, rather than getting a trivial plan</p>
<p>Nice theory, but how does one tell if a particular query has a trivial execution plan? The information is found within the execution plan, the properties of the highest-level operator has an entry &#8216;Optimisation level&#8217; For a trivial plan this will read ‘TRIVIAL’</p>
<p><img style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" title="Trivial plan" src="http://sqlinthewild.co.za/wp-content/uploads/2009/12/Trivialplan.png" border="0" alt="Trivial plan" width="270" height="362" /></p>
<p><span id="more-495"></span>It&#8217;s also, for the brave people who like XML, found within the xml form of the plan</p>
<pre class="brush: xml; title: ; notranslate">&lt;StmtSimple StatementCompId='1' StatementEstRows='11' StatementId='1' StatementOptmLevel='TRIVIAL' StatementSubTreeCost='0.0032941' StatementText='SELECT * FROM forums' StatementType='SELECT' QueryHash='0xB38EBF594006422E' QueryPlanHash='0xCC9AB99E7081C81D'&gt;
&lt;!-- most of the rest of the plan here --&gt;
&lt;/StmtSimple&gt;</pre>
<p>So, are they cached? The way to find that out is to run a variety of queries and see what&#8217;s sitting in the cache afterwards.</p>
<p>I&#8217;m going to clear the procedure cache then run a couple queries against the AdventureWorks database, checking the graphical execution plan for each one. After they&#8217;ve all been run, I&#8217;ll query the plan cache and check the optimisation level of the cached plans,</p>
<p>Query 1:</p>
<pre class="brush: sql; title: ; notranslate">SELECT FirstName, LastName
    FROM Person.Person
    WHERE BusinessEntityID = 42</pre>
<p>According to the graphical plan, this query has a trivial plan.</p>
<p>Query 2:</p>
<pre class="brush: sql; title: ; notranslate">SELECT TOP (10) Name FROM Production.Product</pre>
<p>According to the graphical plan, this query also has a trivial plan.</p>
<p>Query 3:</p>
<pre class="brush: sql; title: ; notranslate">SELECT * FROM Sales.SalesOrderHeader sh
    INNER JOIN sales.SalesOrderDetail sd
        ON sh.SalesOrderID = sd.SalesOrderID
    WHERE sh.ShipDate &gt; '2008/05/25'</pre>
<p>According to the graphical plan, this query has a non-trivial plan, the optimisation level is listed as full.</p>
<pre class="brush: sql; title: ; notranslate">SELECT st.text, qp.query_plan,
     qp.query_plan.value('
       declare default element
       namespace &quot;http://schemas.microsoft.com/sqlserver/2004/07/showplan&quot;;
       (//StmtSimple/@StatementOptmLevel)[1]','varchar(20)') AS OptimisationLevel
    FROM sys.dm_exec_cached_plans cp
        CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp
        CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) st
    WHERE text not like '%sys.dm_exec_cached_plans%'</pre>
<p>This returns 4 rows. All three of the queries that I ran against AdventureWorks, two with optimisation levels of Trivial, one with an optimisation level of Full, and the unparameterised ‘shell’ of the first of the queries.</p>
<p>So it appears that some trivial plans are indeed cached.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2009/12/08/are-trivial-plans-cached/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Estimated rows, actual rows and execution count</title>
		<link>http://sqlinthewild.co.za/index.php/2009/09/22/estimated-rows-actual-rows-and-execution-count/</link>
		<comments>http://sqlinthewild.co.za/index.php/2009/09/22/estimated-rows-actual-rows-and-execution-count/#comments</comments>
		<pubDate>Tue, 22 Sep 2009 10:00:48 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=294</guid>
		<description><![CDATA[It&#8217;s often said that a major discrepancy between estimated and actual row counts in a query&#8217;s execution plan is a sign of inaccurate statistics or a poor cardinality estimate and that it&#8217;s a sign of a problem. This is generally true, however there are places where the estimates and actual rows will differ, often quite [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s often said that a major discrepancy between estimated and actual row counts in a query&#8217;s execution plan is a sign of inaccurate statistics or a poor cardinality estimate and that it&#8217;s a sign of a problem. This is generally true, however there are places where the estimates and actual rows will differ, often quite dramatically, without it been a problem. The reason for this is that these two values show slightly different things.</p>
<p>Let&#8217;s take a look at an example. (table creation code at the end of the post)</p>
<pre class="brush: sql; title: ; notranslate">select bt.id, bt.SomeColumn, st.SomeArbDate
from dbo.BigTable bt
inner join dbo.SmallerTable st on bt.SomeColumn = st.LookupColumn
where bt.id between 5000 and 5100</pre>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2009/09/EstimatedActual.png"><img class="alignnone size-medium wp-image-338" style="border: 1px solid black;" title="Estimated Actual discrepency" src="http://sqlinthewild.co.za/wp-content/uploads/2009/09/EstimatedActual-300x213.png" alt="Estimated Actual discrepency" width="300" height="213" /></a></p>
<p>Estimated rows = 1, actual rows = 101. That&#8217;s a large discrepancy, but what caused it? It&#8217;s not out of date statistics (a usual cause) because the table has only just been created, so why is the estimation so far from the actual.</p>
<p><span id="more-294"></span>Let&#8217;s take a closer look at that seek. The seek predicate is an equality match on LookupColumn. That can only return 1 row because when the table was populated it was populated with unique values for that column (though the index on that column is not defined unique). So the estimated row count is dead-on, the index seek will return  a single row. Now the question is where that 101 for the actual comes from.</p>
<p>This seek is on the inner table of a nested loop join. The way the nested loop join works is to query the outer table of the join and then to query the inner table once for each row returned by the outer table. A look at the details of the clustered index scan that defines the outer table of the nested loop shows that it returns 101 rows.</p>
<p><a href="http://sqlinthewild.co.za/wp-content/uploads/2009/09/OuterTable.png"><img class="alignnone size-medium wp-image-340" style="border: 1px solid black;" title="OuterTable" src="http://sqlinthewild.co.za/wp-content/uploads/2009/09/OuterTable-300x205.png" alt="OuterTable" width="300" height="205" /><br />
</a></p>
<p>Since the outer table returns 101 rows, the seek on the inner table must be done 101 times. That&#8217;s supported by the execution count shown on the inner seek. That&#8217;s where the discrepancy between actual and estimated rows comes from.</p>
<p>When an operator is executed multiple times as part of the query execution, the estimated row count refers to the number of rows that the optimiser estimates will be affected per execution. The actual row count refers to the total number of rows that the operator affected, cumulative over all executions. So when checking to see if there&#8217;s a major discrepancy between estimated and actual rows counts, the actual row count has to be divided by the number of executions.</p>
<p>That&#8217;s fine when using SQL 2008&#8242;s management studio, which exposes the execution count of an operator in the tooltip of that operator. SQL 2005&#8242;s management studio did not display the execution count anywhere convenient, though it is present in the XML of the plan. This is purely a feature of the version of Management Studio. The SQL 2008 management studio will display the execution count regardless of whether it&#8217;s connected to SQL 2005 or to SQL 2008.</p>
<p>For those still using SQL 2005&#8242;s tools, if you want the execution count, this is where to look:</p>
<pre class="brush: xml; title: ; notranslate">&lt;RelOp AvgRowSize=&quot;15&quot; EstimateCPU=&quot;0.0001581&quot; EstimateIO=&quot;0.003125&quot; EstimateRebinds=&quot;85.432&quot; EstimateRewinds=&quot;0&quot; EstimateRows=&quot;1&quot; LogicalOp=&quot;Index Seek&quot; NodeId=&quot;2&quot; Parallel=&quot;false&quot; PhysicalOp=&quot;Index Seek&quot; EstimatedTotalSubtreeCost=&quot;0.0480212&quot; TableCardinality=&quot;3956&quot;&gt;
&lt;OutputList&gt;
&lt;ColumnReference Database=&quot;[Testing]&quot; Schema=&quot;[dbo]&quot; Table=&quot;[SmallerTable]&quot; Alias=&quot;[st]&quot; Column=&quot;SomeArbDate&quot; /&gt;
&lt;/OutputList&gt;
&lt;RunTimeInformation&gt;
&lt;RunTimeCountersPerThread Thread=&quot;0&quot; ActualRows=&quot;101&quot; ActualEndOfScans=&quot;101&quot; ActualExecutions=&quot;101&quot; /&gt;
&lt;/RunTimeInformation&gt;</pre>
<p>The number of executions, along with the actual row count is contained within the XML node RunTimeInformation. Obviously this node will not be present when looking at an estimated execution plan or an execution plan retrieved from the plan cache, as neither contains any run-time information.</p>
<p>Table creation code.</p>
<p>Edit: In the original post I left 2 indexes out of the table creation, which changed the behaviour of the query completely (hash joins instead of nested loop). If you tried to reproduce my results and couldn&#8217;t, you should be able to now with the correct indexes.</p>
<pre class="brush: sql; title: ; notranslate">Create Table BigTable (
id int identity primary key,
SomeColumn char(4),
Filler char(100)
)

Create Table SmallerTable (
id int identity primary key,
LookupColumn char(4),
SomeArbDate Datetime default getdate()
)

INSERT INTO BigTable (SomeColumn)
SELECT top 250000
char(65+FLOOR(RAND(a.column_id *5645 + b.object_id)*10)) + char(65+FLOOR(RAND(b.column_id *3784 + b.object_id)*12)) +
char(65+FLOOR(RAND(b.column_id *6841 + a.object_id)*12)) + char(65+FLOOR(RAND(a.column_id *7544 + b.object_id)*8))
from master.sys.columns a cross join master.sys.columns b

INSERT INTO SmallerTable (LookupColumn)
SELECT DISTINCT SomeColumn
FROM BigTable TABLESAMPLE (25 PERCENT)
GO

CREATE NONCLUSTERED INDEX [idx_BigTable_SomeColumn]
  ON [dbo].[BigTable] ([SomeColumn] ASC)
GO

CREATE NONCLUSTERED INDEX [idx_SmallerTable_LookupColumn]
  ON [dbo].[SmallerTable] ([LookupColumn] ASC)
  INCLUDE ( [SomeArbDate])
GO
</pre>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 535px; width: 1px; height: 1px;">From    Subject    Received    Size<br />
Gideon van Zyl &#8211; XE    C-Track info/docs 4 SAPS    15:53    24 KB</div>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2009/09/22/estimated-rows-actual-rows-and-execution-count/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Estimated and Actual execution plan revisited</title>
		<link>http://sqlinthewild.co.za/index.php/2009/02/19/estimated-and-actual-execution-plan-revisited/</link>
		<comments>http://sqlinthewild.co.za/index.php/2009/02/19/estimated-and-actual-execution-plan-revisited/#comments</comments>
		<pubDate>Thu, 19 Feb 2009 17:42:01 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=211</guid>
		<description><![CDATA[After an interesting discussion on SQLServerCentral last week, I realised that the terms &#8216;estimated execution plan&#8217; and &#8216;actual execution plan&#8217; are perhaps a little bit misleading. The only thing estimated about the estimated execution plan is the rowcounts, costs and row size. The plan itself isn&#8217;t an estimate. It&#8217;s not as if the optimiser, when [...]]]></description>
			<content:encoded><![CDATA[<p>After an <a href="http://www.sqlservercentral.com/Forums/Topic655518-360-1.aspx">interesting discussion</a> on SQLServerCentral last week, I realised that the terms &#8216;estimated execution plan&#8217; and &#8216;actual execution plan&#8217; are perhaps a little bit misleading.</p>
<p>The only thing estimated about the estimated execution plan is the rowcounts, costs and row size. The plan itself isn&#8217;t an estimate. It&#8217;s not as if the optimiser, when asked for an estimated plan, does a less thorough job than when asked to compile a plan for a query&#8217;s execution.</p>
<p>The two forms of execution plan are better described as &#8216;execution plan with run-time information&#8217; and &#8216;execution plan without run-time information&#8217;</p>
<p>When, in Management Studio, someone clicks the &#8216;display estimated execution plan&#8217; button, the query is submitted to SQL Server, parsed and bound, algebratised and optimised just as if it was going to be executed. But the query is not executed, and as such, the plan when returned contains no run time information.</p>
<p>If there is a matching cached query plan, that cached plan is what&#8217;s returned and no optimisation is done. This can be seen by using profiler with the Cache hit, cache miss and cache insert events being traced.</p>
<p>When, in Management Studio, the query is run with the execution plan option enabled, the query is submitted to SQL Server, parsed and bound, algebratised, optimised and executed. The returned plan does contain the run-time for that specific execution, hence the plan contains things like &#8216;actual row count, actual IO cost&#8217;, etc</p>
<p>If there&#8217;s a matching query plan in cache then that cached plan will be used for the query&#8217;s execution and will be the one returned, though with the run-time information added</p>
<p>When a plan is cached, only the compile-time information is cached. The detailed run-time information on the actual number of rows, costs and the like is (I believe) discarded after updating the aggregated query stats. Hence, when you retrieve a query from the plan cache, it will not contain the run-time information. Consider a plan that&#8217;s been used 20 times. Which execution&#8217;s run-time information would it contain? Remember that there&#8217;s only one plan in cache per procedure.</p>
<p>Hence, a plan fetched from will be identical to the plan returned by requesting the estimated execution plan for a specific query (Providing there&#8217;s nothing happening to invalidate the cached plan)</p>
<p>Profiler can capture (depending on event) the plan without the run-time information or the plan with the run-time information. There&#8217;s a nice table in chapter 2 of one of <a href="http://www.amazon.com/Inside-Microsoft-SQL-Server-2005/dp/0735623139/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1235065233&amp;sr=8-1">Itzik&#8217;s books</a> that shows the various profiler events, when they fire and what they return.</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2009/02/19/estimated-and-actual-execution-plan-revisited/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A Bookmark lookup, by any other name&#8230;</title>
		<link>http://sqlinthewild.co.za/index.php/2009/01/27/a-bookmark-lookup-by-any-other-name/</link>
		<comments>http://sqlinthewild.co.za/index.php/2009/01/27/a-bookmark-lookup-by-any-other-name/#comments</comments>
		<pubDate>Tue, 27 Jan 2009 16:53:52 +0000</pubDate>
		<dc:creator>Gail</dc:creator>
				<category><![CDATA[Execution Plans]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Syndication]]></category>

		<guid isPermaLink="false">http://sqlinthewild.co.za/?p=199</guid>
		<description><![CDATA[I think I may have confused some people by talking about bookmark lookups. I&#8217;ll attempt to clarify things. The operator that I&#8217;m talking about is the one that fetches extra columns from the clustered index when the nonclustered index that&#8217;s used to retrieve the rows doesn&#8217;t have all of the columns required. In SQL 2000, [...]]]></description>
			<content:encoded><![CDATA[<p>I think I may have confused some people by talking about bookmark lookups. I&#8217;ll attempt to clarify things.</p>
<p>The operator that I&#8217;m talking about is the one that fetches extra columns from the clustered index when the nonclustered index that&#8217;s used to retrieve the rows doesn&#8217;t have all of the columns required.</p>
<p>In SQL 2000, that operator appeared in the execution plan as a bookmark lookup and it appeared as follows:</p>
<p><img class="alignnone size-full wp-image-200" title="bookmark-lookup" src="http://sqlinthewild.co.za/wp-content/uploads/2009/01/bookmark-lookup.png" alt="" width="163" height="85" /></p>
<p>In SQL 2005, the name was changed, and the bookmark lookup no longer appeared in the execution plan. In it&#8217;s place appeared a clustered index seek, joined back to the original index seek by a nested loop join. It appeared as follows (the highlighted operator is the &#8216;bookmark lookup&#8217;)</p>
<p><img class="alignnone size-full wp-image-201" title="clustered-index-seek" src="http://sqlinthewild.co.za/wp-content/uploads/2009/01/clustered-index-seek.png" alt="" width="375" height="186" /></p>
<p>This change made it harder to see what was going on as clustered index seeks are usually considered &#8216;good&#8217;. The trick to see if it is actually a &#8216;bookmark lookup&#8217; is to look at the objects involved. When the nonclustered index seek and the clustered index seek are both on the same table, then it&#8217;s a &#8216;bookmark lookup&#8217;</p>
<p>I don&#8217;t recall what this appeared as when the base table was a heap, not a cluster.</p>
<p>In SQL 2005 SP2, the name of the operator was changed again, now appearing as a key lookup (when the base table has a clustered index) or a RID lookup (when the base table is a heap). It now looks like this:</p>
<p><img class="alignnone size-full wp-image-202" title="keylookup" src="http://sqlinthewild.co.za/wp-content/uploads/2009/01/keylookup.png" alt="" width="356" height="167" /></p>
<p>The thing to note is that it&#8217;s not the version of the server that&#8217;s important. The format of the XML for the exec plan has not changed since SQL 2005 RTM (I can and have created a .sqlplan file from SQL 2008 and opened that file in SQL 2005&#8242;s management studio).</p>
<p>It&#8217;s the version of management studio that affects how the execution plans are displayed. If the server is SQL 2005 SP3, but the client tools are still RTM, the bookmark lookup will appear as a clustered index seek. Another reason to patch the client as well as the server</p>
<p>I hope that clears up some of the confusion around the naming. So, in future, what should I refer to this as? A bookmark lookup? A Key lookup?</p>
]]></content:encoded>
			<wfw:commentRss>http://sqlinthewild.co.za/index.php/2009/01/27/a-bookmark-lookup-by-any-other-name/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>

