<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://www2.sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Kalen Delaney : showplan</title><link>http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/showplan/default.aspx</link><description>Tags: showplan</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>Geek City: Ordered Seeks and Scans</title><link>http://www2.sqlblog.com/blogs/kalen_delaney/archive/2011/01/21/ordered-seeks-and-scans.aspx</link><pubDate>Fri, 21 Jan 2011 23:28:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:32833</guid><dc:creator>Kalen Delaney</dc:creator><slash:comments>5</slash:comments><comments>http://www2.sqlblog.com/blogs/kalen_delaney/comments/32833.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/kalen_delaney/commentrss.aspx?PostID=32833</wfw:commentRss><description>I got a couple of really great questions during my SSWUG Workshop this morning , as I was discussing seeks and scans, and since the answers to the two questions are very related, I decided to address both of them in more detail in this blog post. Most...(&lt;a href="http://www2.sqlblog.com/blogs/kalen_delaney/archive/2011/01/21/ordered-seeks-and-scans.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=32833" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/training/default.aspx">training</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/showplan/default.aspx">showplan</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/indexes/default.aspx">indexes</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/seminars/default.aspx">seminars</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/query+plans/default.aspx">query plans</category></item><item><title>TSQL Tuesday #8: Those who can, do, and those who want to learn more, teach!</title><link>http://www2.sqlblog.com/blogs/kalen_delaney/archive/2010/07/13/those-who-want-to-learn-more-teach.aspx</link><pubDate>Tue, 13 Jul 2010 14:02:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:26987</guid><dc:creator>Kalen Delaney</dc:creator><slash:comments>6</slash:comments><comments>http://www2.sqlblog.com/blogs/kalen_delaney/comments/26987.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/kalen_delaney/commentrss.aspx?PostID=26987</wfw:commentRss><description>It's time for the eighth T-SQL Tuesday , managed this time by Robert Davis at SQL Server Central . This time, the topic was announced very late, and I was just starting to write a blog post of my own when I saw it. Fortunately, my topic easily fits into...(&lt;a href="http://www2.sqlblog.com/blogs/kalen_delaney/archive/2010/07/13/those-who-want-to-learn-more-teach.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=26987" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/showplan/default.aspx">showplan</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/query+plans/default.aspx">query plans</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/T-SQL+Tuesday/default.aspx">T-SQL Tuesday</category></item><item><title>Did You Know: My next web seminar is this Wednesday!</title><link>http://www2.sqlblog.com/blogs/kalen_delaney/archive/2010/06/27/my-next-web-seminar-is-this-wednesday.aspx</link><pubDate>Mon, 28 Jun 2010 03:40:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:26487</guid><dc:creator>Kalen Delaney</dc:creator><slash:comments>2</slash:comments><comments>http://www2.sqlblog.com/blogs/kalen_delaney/comments/26487.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/kalen_delaney/commentrss.aspx?PostID=26487</wfw:commentRss><description>I know, I haven't made a geeky post in a while... but I promise that I'll do one after this seminar. I'll take some of the best questions that I get during the chat and write them up here. Query Plans Workshop (June 30, 2010) Learn about basic elements,...(&lt;a href="http://www2.sqlblog.com/blogs/kalen_delaney/archive/2010/06/27/my-next-web-seminar-is-this-wednesday.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=26487" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/training/default.aspx">training</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/showplan/default.aspx">showplan</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/seminars/default.aspx">seminars</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/query+tuning/default.aspx">query tuning</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/query+plans/default.aspx">query plans</category></item><item><title>Geek City: String Statistics</title><link>http://www2.sqlblog.com/blogs/kalen_delaney/archive/2008/02/13/string-statistics.aspx</link><pubDate>Wed, 13 Feb 2008 22:18:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:5065</guid><dc:creator>Kalen Delaney</dc:creator><slash:comments>6</slash:comments><comments>http://www2.sqlblog.com/blogs/kalen_delaney/comments/5065.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/kalen_delaney/commentrss.aspx?PostID=5065</wfw:commentRss><description>&lt;P&gt;If you’ve ever run DBCC SHOW_STATISTICS,&amp;nbsp; you know you get 3 sections of information back. The first section is basic information about the last time the statistics were updated, the number of rows, the number of steps, etc. The second section is density information for each left-based subset of columns. The third section is the histogram for the first column in the statistics. I won't be going into detail on what any of these things mean (i.e. steps, density, histogram), but you can get lots more information from this whitepaper: 
&lt;P&gt;&lt;A href="http://www.microsoft.com/technet/prodtechnol/sql/2005/qrystats.mspx"&gt;Statistics Used by the Query Optimizer in Microsoft SQL Server 2005&lt;/A&gt; 
&lt;P&gt;What you might &lt;STRONG&gt;not&lt;/STRONG&gt; know is that you can get each of the three sections independently by adding an option to the DBCC command: 
&lt;P&gt;DBCC SHOW_STATISTICS ('AdventureWorks.Person.Contact',&lt;BR&gt;'IX_Contact_EmailAddress') &lt;B&gt;WITH STAT_HEADER&lt;/B&gt;; 
&lt;P&gt;DBCC SHOW_STATISTICS ('AdventureWorks.Person.Contact',&lt;BR&gt;'IX_Contact_EmailAddress') &lt;B&gt;WITH DENSITY_VECTOR&lt;/B&gt;; 
&lt;P&gt;DBCC SHOW_STATISTICS ('AdventureWorks.Person.Contact',&lt;BR&gt;'IX_Contact_EmailAddress') &lt;B&gt;WITH HISTOGRAM&lt;/B&gt;; 
&lt;P&gt;These options are documented as part of the DBCC SHOW_STATISTICS command for SQL Server 2005. These options were actually available in SQL Server 2000, but they just aren’t documented.&amp;nbsp; 
&lt;P&gt;In SQL Server 2005, the first section contains a column of output called "String Index", which I like to call "String Statistics". I just wrote an article for &lt;A href="http://www.sqlcommunity.com/"&gt;www.SQLCommunity.com&lt;/A&gt; about what these string statistics do for you. 
&lt;P&gt;&lt;A title=http://www.sqlcommunity.com/default.aspx?tabid=77&amp;amp;id=178 href="http://www.sqlcommunity.com/default.aspx?tabid=77&amp;amp;id=178"&gt;http://www.sqlcommunity.com/default.aspx?tabid=77&amp;amp;id=178&lt;/A&gt; 
&lt;P&gt;Have fun! 
&lt;P&gt;&lt;FONT color=#ff00ff&gt;~Kalen&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=5065" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/nonclustered+index/default.aspx">nonclustered index</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/showplan/default.aspx">showplan</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/statistics/default.aspx">statistics</category></item><item><title>Did You Know? Estimated vs Actual Plans</title><link>http://www2.sqlblog.com/blogs/kalen_delaney/archive/2007/07/30/did-you-know-estimated-vs-actual-plans.aspx</link><pubDate>Mon, 30 Jul 2007 18:41:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:1999</guid><dc:creator>Kalen Delaney</dc:creator><slash:comments>5</slash:comments><comments>http://www2.sqlblog.com/blogs/kalen_delaney/comments/1999.aspx</comments><wfw:commentRss>http://www2.sqlblog.com/blogs/kalen_delaney/commentrss.aspx?PostID=1999</wfw:commentRss><description>&lt;P&gt;In my previous post, I mentioned that it is important to understand the difference between estimated and actual query plans, so I decided to go into a few more details regarding the differences. &lt;/P&gt;
&lt;P&gt;Optimization takes place&amp;nbsp;before execution, so in one sense, any query plan is an estimated plan. But when you request an actual plan, SQL Server will actually execute each statement as it displays the plan, so&amp;nbsp;the plan&amp;nbsp;for subsequent statements can change after the earlier statements are executed.&amp;nbsp; In addition, SQL Server adds additional information to the actual plan, after the execution takes place. As mentioned last time, the actual plan includes the number of processors used to execute the query. It will also include the number of rows returned from each step of the plan as it is executed, and the number of times each step is executed.&amp;nbsp; If you are executing a plan that has already been cached and is now being reused, the actual plan will include both the parameter values the plan was originally compiled with and the parameter values used during execution. &lt;/P&gt;
&lt;P&gt;You can request that SQL Server display ESTIMATED plans without executing the query with any of the following options:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;SET SHOWPLAN_TEXT ON &lt;/P&gt;
&lt;P&gt;SET SHOWPLAN_ALL ON&lt;/P&gt;
&lt;P&gt;SET SHOWPLAN_XML ON&lt;/P&gt;
&lt;P&gt;For graphical estimated plans, you can use the Query | Display Estimated Execution Plan menu option. This can be invoked with a toolbar button, or with Cntl-L. &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;You can request that SQL Server display actual plans with any of the following options:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;SET STATISTICS PROFILE ON &lt;/P&gt;
&lt;P&gt;SET STATISTICS XML ON &lt;/P&gt;
&lt;P&gt;For graphical actual plans, you can use the Query | Include Actual Execution Plan menu option. This can be invoked with a toolbar button, or with Cntl-K. &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I'll show you 3 examples of ways in which the actual plan can be useful. They require that you have the AdventureWorks database installed.&lt;/P&gt;
&lt;P&gt;Example 1:&lt;/P&gt;
&lt;P&gt;First, create a stored procedure called 'testtemp' that will build a temporary table.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;USE AdventureWorks&lt;BR&gt;GO&lt;BR&gt;IF EXISTS (SELECT 1 FROM sys.procedures&lt;BR&gt;WHERE name = 'testtemp')&lt;BR&gt;DROP PROC testtemp&lt;BR&gt;GO&lt;BR&gt;CREATE PROC testtemp&lt;BR&gt;(@p int)&lt;BR&gt;AS&lt;BR&gt;SELECT * INTO #t&lt;BR&gt;FROM Sales.SalesOrderDetail &lt;BR&gt;CREATE INDEX t_index ON #t (ProductID)&lt;BR&gt;SELECT * FROM #t&lt;BR&gt;WHERE ProductID &amp;lt; @p&lt;BR&gt;RETURN &lt;BR&gt;GO&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Try to look at the estimated plan for the procedure, and you will get an error because the plan for the SELECT cannot be generated when the temp table #t does not exist: 
&lt;BLOCKQUOTE&gt;
&lt;P&gt;-- Estimated plan &lt;BR&gt;SET SHOWPLAN_TEXT ON&amp;nbsp;&lt;BR&gt;GO&lt;BR&gt;EXEC testtemp 896&lt;BR&gt;-- error is generated&lt;BR&gt;GO&lt;BR&gt;SET SHOWPLAN_TEXT&amp;nbsp;OFF&amp;nbsp;&lt;BR&gt;GO &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Now look at the actual plan and you will see plans for the temp table creation and for the SELECT from the temp table.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;SET STATISTICS PROFILE ON&amp;nbsp;&lt;BR&gt;GO&lt;BR&gt;EXEC testtemp 896&lt;BR&gt;GO&lt;BR&gt;SET STATISTICS PROFILE OFF&amp;nbsp;&lt;BR&gt;GO &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Example 2:&lt;/P&gt;
&lt;P&gt;One of the main reasons that the actual plan may different from the estimated plan is because of data changes to your data. In general (and assuming you have the option 'auto update statistics' enabled) if more than 20% of the data in a table changes, the optimizer will detect stale statistics and update them automatically. The updated statistics will then trigger a recompile. &lt;/P&gt;
&lt;P&gt;This example makes a copy of Sales.SalesOrderDetail in the AdventureWorks database and builds an index on the table.&amp;nbsp;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;SELECT * INTO NewOrders &lt;BR&gt;FROM Sales.SalesOrderDetail &lt;BR&gt;GO 
&lt;P&gt;CREATE INDEX IX_NewOrders_ProductID on NewOrders(ProductID) &lt;BR&gt;GO 
&lt;P&gt;SET SHOWPLAN_ALL ON -- Estimated Plan &lt;BR&gt;GO 
&lt;P&gt;-- The estimated plan shows us that a seek is done on the SELECT from NewOrders because at optimization time &lt;BR&gt;--&amp;nbsp;there are only a few rows that have a ProductID value of 897. 
&lt;P&gt;BEGIN TRAN &lt;BR&gt;UPDATE NewOrders &lt;BR&gt;SET ProductID = 897 &lt;BR&gt;WHERE ProductID between 800 and 900 
&lt;P&gt;SELECT OrderQty, CarrierTrackingNumber&lt;BR&gt;FROM NewOrders &lt;BR&gt;WHERE ProductID = 897 &lt;BR&gt;ROLLBACK TRAN &lt;BR&gt;GO 
&lt;P&gt;SET SHOWPLAN_ALL OFF &lt;BR&gt;GO 
&lt;P&gt;&amp;nbsp;&lt;/P&gt;-- The actual plan shows us that a table scan is done on the SELECT from NewOrders because after the update is executed, &lt;BR&gt;--&amp;nbsp; statistics are updated and the query is recompiled. Now there are lots of rows&amp;nbsp;that have a ProductID value of 897. 
&lt;P&gt;SET STATISTICS PROFILE ON -- Actual Plan &lt;BR&gt;GO 
&lt;P&gt;BEGIN TRAN &lt;BR&gt;UPDATE NewOrders &lt;BR&gt;SET ProductID = 897 &lt;BR&gt;WHERE ProductID between 800 and 900 &lt;/P&gt;
&lt;P&gt;SELECT OrderQty, CarrierTrackingNumber &lt;BR&gt;FROM NewOrders &lt;BR&gt;WHERE ProductID = 897 &lt;BR&gt;ROLLBACK TRAN &lt;BR&gt;GO &lt;/P&gt;
&lt;P&gt;SET STATISTICS PROFILE OFF &lt;BR&gt;GO &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Example 3: 
&lt;P&gt;For this example, you can create a stored procedure that uses a parameter to determine which rows from the Sales.SalesOrderDetail table to select. 
&lt;BLOCKQUOTE&gt;
&lt;P&gt;USE AdventureWorks&lt;BR&gt;GO&lt;BR&gt;IF EXISTS (SELECT 1 FROM sys.procedures&lt;BR&gt;WHERE name = 'GetProducts')&lt;BR&gt;DROP PROC GetProducts&lt;BR&gt;GO&lt;BR&gt;CREATE PROC GetProducts&lt;BR&gt;(@p int)&lt;BR&gt;AS&lt;BR&gt;SELECT * &lt;BR&gt;FROM Sales.SalesOrderDetail &lt;BR&gt;WHERE ProductID = @p&lt;BR&gt;RETURN &lt;BR&gt;GO&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The first time you run the procedure, a plan will be built based on the first parameter. The second time you run the procedure, the original plan will be used, and the statistics should show that SQL Server is performing more reads than there are pages in the table. (There are 1238 pages in the table.) 
&lt;BLOCKQUOTE&gt;
&lt;P&gt;SET STATISTICS IO ON&lt;BR&gt;GO&lt;BR&gt;EXEC GetProducts 710&amp;nbsp; -- using nc index takes 145 logical reads&lt;BR&gt;GO&lt;BR&gt;EXEC GetProducts 707&amp;nbsp; -- using nc index takes 9458 logical reads. &lt;BR&gt;GO &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;You can look at the actual plan in XML to see the parameters used for compile and execute.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;SET STATISTICS XML ON&lt;BR&gt;GO&lt;BR&gt;EXEC GetProducts 707 &lt;BR&gt;GO&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Near the bottom of the XML document, you should see the following:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&amp;lt;ParameterList&amp;gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;ColumnReference Column="@p" ParameterCompiledValue="(710)" ParameterRuntimeValue="(707)" /&amp;gt;&lt;/P&gt;
&lt;P&gt;&amp;lt;/ParameterList&amp;gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Seeing the different values used for compilation and execution can lead you to suspect an issue with parameter sniffing. There are several ways around this problem, but that's the topic for another day. &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Have fun!&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#ff00ff&gt;&lt;STRONG&gt;~Kalen&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://www2.sqlblog.com/aggbug.aspx?PostID=1999" width="1" height="1"&gt;</description><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/parameter+sniffing/default.aspx">parameter sniffing</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/temp+tables/default.aspx">temp tables</category><category domain="http://www2.sqlblog.com/blogs/kalen_delaney/archive/tags/showplan/default.aspx">showplan</category></item></channel></rss>