<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://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>Benjamin Nevarez : Performance</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx</link><description>Tags: Performance</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>Fooling the Query Optimizer</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2010/01/13/fooling-the-query-optimizer.aspx</link><pubDate>Thu, 14 Jan 2010 04:57:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:21032</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>4</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/21032.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=21032</wfw:commentRss><description>&lt;P class=MsoNormal&gt;Did you ever wanted to know which execution plans the Query Optimizer would generate for your queries should your tables have millions of records? You can actually generate those plans by using the undocumented ROWCOUNT and PAGECOUNT options of the UPDATE STATISTICS statement. These options can be used on small or empty tables and can be helpful&amp;nbsp;for testing&amp;nbsp;in some scenarios where you may not want to spent time or disk space creating big tables.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;By using this method you are tricking the Query Optimizer as it will generate execution plans using cardinality estimations as if the table really had millions of records. Note that this option, available since SQL Server 2005, only helps in creating the execution plan for your queries. Actually running the query will use the real data and of course will execute faster than a table with millions of records.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;UPDATE STATISTICS WITH ROWCOUNT, PAGECOUNT does not change the table statistics, only the counter of number of rows and pages of a table. But the Query Optimizer uses this information to estimate the cardinality of queries as I will show later. Also keep in mind that these are undocumented and unsupported options and should not be used in any production environment.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Let us see an example. Run the following query to create a new table on the AdventureWorks database&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;into&lt;/SPAN&gt; dbo&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;Address &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;from&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; Person&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;Address &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;Inspect the number of rows by running the following queries. It must show 19,614 rows.&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; &lt;SPAN style="COLOR:green;"&gt;sys&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:green;"&gt;partitions &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:fuchsia;"&gt;object_id&lt;/SPAN&gt; &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; &lt;SPAN style="COLOR:fuchsia;"&gt;object_id&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;'dbo.Address'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;) &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; &lt;SPAN style="COLOR:green;"&gt;sys&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:green;"&gt;dm_db_partition_stats 
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:fuchsia;"&gt;object_id&lt;/SPAN&gt; &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; &lt;SPAN style="COLOR:fuchsia;"&gt;object_id&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;'dbo.Address'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;) &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal&gt;Run the following query&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; dbo&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;Address &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; city &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; &lt;SPAN style="COLOR:red;"&gt;'London' &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal&gt;Running this query will create new statistics for the city column and will show the following plan. Note that the estimated number of rows is 434 and it is using a simple Table Scan operator&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image002_5DF90560.jpg"&gt;&lt;IMG title=clip_image002 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:inline;BORDER-LEFT:0px;BORDER-BOTTOM:0px;" height=142 alt=clip_image002 src="http://sqlblog.com/blogs/ben_nevarez/clip_image002_thumb_1ADEBA63.jpg" width=334 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;We can see where the Query Optimizer is getting the estimated number of rows by inspecting the statistics object. Run this query to see the name of the statistics object&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; &lt;SPAN style="COLOR:green;"&gt;sys&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:green;"&gt;stats &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:fuchsia;"&gt;object_id&lt;/SPAN&gt; &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; &lt;SPAN style="COLOR:fuchsia;"&gt;object_id&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;'dbo.Address'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;) &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal&gt;Then use the displayed statistics object name in the following statement (the name may be different in your case)&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;dbcc&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;show_statistics&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;'dbo.Address'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; _WA_Sys_00000004_46136164&lt;SPAN style="COLOR:gray;"&gt;) &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal&gt;By looking at the histogram you can find the value 434 on EQ_ROWS for the RANGE_HI_KEY value ‘London’ (Statistics and histograms are explained on previous posts in this blog)&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image004_08C42013.jpg"&gt;&lt;IMG title=clip_image004 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:block;FLOAT:none;MARGIN-LEFT:auto;BORDER-LEFT:0px;MARGIN-RIGHT:auto;BORDER-BOTTOM:0px;" height=93 alt=clip_image004 src="http://sqlblog.com/blogs/ben_nevarez/clip_image004_thumb_76A985C2.jpg" width=540 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Now run the UPDATE STATISTICS WITH ROWCOUNT, PAGECOUNT (you can specify any other value for rowcount and pagecount)&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;update&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;statistics&lt;/SPAN&gt; dbo&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;Address&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;with&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;rowcount&lt;/SPAN&gt; &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 1000000&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;pagecount&lt;/SPAN&gt; &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 100000 &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal&gt;If you inspect the number of rows again from sys.partitions or sys.dm_db_partition_stats, as shown previously, it will now show 1,000,000 rows. sys.dm_db_partition_stats also shows the new number of pages. Clear the plan cache and run the query again&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;dbcc&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; freeproccache &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;go &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; dbo&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;Address &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; city &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; &lt;SPAN style="COLOR:red;"&gt;'London' &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal&gt;Note that the estimated number of rows has changed from 434 to 22,127.1 and that a different plan was generated using this new cardinality estimation. The Query Optimizer decided to parallelize this plan. But this is a very simple query, more dramatic plan changes can happen with more complex queries.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image006_3DC0D532.jpg"&gt;&lt;IMG title=clip_image006 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:block;FLOAT:none;MARGIN-LEFT:auto;BORDER-LEFT:0px;MARGIN-RIGHT:auto;BORDER-BOTTOM:0px;" height=135 alt=clip_image006 src="http://sqlblog.com/blogs/ben_nevarez/clip_image006_thumb_637B0FB6.jpg" width=508 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;After execution the actual number of rows obviously is still is 434 but the Query Optimizer is not able to see this value.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;If you look at the statistics object again, using DBCC SHOW_STATISTICS as shown before, the histogram has not changed. One way to obtain the estimated number of rows shown in the new execution plan is calculating the percentage or fraction of rows for the value ‘London’ from the statistics sample, which in this case is 19,614, as shown on the header of the statistics object. So the fraction is 434 / 19,614 or 022127052. Then obtain the same percentage from the new “current” number of rows which is 1,000,000 calculated as 1,000,000 * 0.022127052 and we get 22,127.1 which is the estimated number of rows displayed in the plan.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Finally, drop the table you just created&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;drop&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;table&lt;/SPAN&gt; dbo&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;Address &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=21032" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Query+Optimizer/default.aspx">Query Optimizer</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Statistics/default.aspx">Statistics</category></item><item><title>Database Engine Tuning Advisor and the Query Optimizer</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2009/11/11/database-engine-tuning-advisor-and-the-query-optimizer.aspx</link><pubDate>Thu, 12 Nov 2009 03:47:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:18767</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>2</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/18767.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=18767</wfw:commentRss><description>&lt;p class="MsoNormal"&gt;Did you know that the Database Engine Tuning Advisor (DTA) uses the Query Optimizer to help you to create indexes, indexed views, and partitions for your databases? The DTA uses the Query Optimizer to estimate the cost of queries so it can select the choices with the lowest estimated cost. But, how can the Query Optimizer estimate the cost of a query using, for example, an index that does not exist yet?&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Creating indexes on a DTA session could be very expensive and can create some other performance problems in your database. In addition to that, when the Query Optimizer uses indexes to estimate the cost of a query, it uses only the index statistics; it does not need to access the index data. &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;So, to avoid creating real indexes during a DTA session, SQL Server has a special kind of indexes called hypothetical indexes. Hypothetical indexes are not real indexes, they only contain statistics and can be created with the undocumented command CREATE INDEX WITH STATISTICS_ONLY. This command only creates the statistics for the index. &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;You may not be able to see these indexes during a DTA session because they are dropped automatically. But you can see the CREATE INDEX WITH STATISTICS_ONLY and DROP INDEX commands if you run Profiler to see what the DTA is doing. You can also create these indexes manually as I will show you later.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Hypothetical index have been available in previous versions of SQL Server where they were used by the DTA predecessor, the Index Tuning Wizard.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Let us take a quick tour to some of these concepts here. Create a new table on the AdventureWorks database&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt;       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;into&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;from&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; sales&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Copy the following query and save it to a file&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt; &lt;span style="color:blue;"&gt;from&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;where&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;span style="color:gray;"&gt;=&lt;/span&gt; 897      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Open a new DTA session. You can optionally run a Profiler session if you want to inspect what the DTA is doing. On workload file select the file containing the SQL statement that you just created. Specify AdventureWorks both for the database to tune and for the database for workload analysis. Click the Start Analysis button.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;When the DTA analysis finishes run this query to inspect the contents of the msdb..DTA_reports_query table&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt; &lt;span style="color:blue;"&gt;from&lt;/span&gt; msdb&lt;span style="color:gray;"&gt;..&lt;/span&gt;DTA_reports_query      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;a href="http://sqlblog.com/blogs/ben_nevarez/clip_image002_1CC91800.jpg"&gt;&lt;img title="clip_image002" style="border-right:0px;border-top:0px;display:block;float:none;margin-left:auto;border-left:0px;margin-right:auto;border-bottom:0px;" height="45" alt="clip_image002" src="http://sqlblog.com/blogs/ben_nevarez/clip_image002_thumb_3EE537A7.jpg" width="507" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Notice that the table contains some information like the query that was tuned and the current and recommended cost. The current cost, 1.2434, is easy to obtain by directly requesting an estimated execution plan for the query&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="text-align:center;" align="center"&gt;&lt;a href="http://sqlblog.com/blogs/ben_nevarez/clip_image004_2302A52E.jpg"&gt;&lt;img title="clip_image004" style="border-right:0px;border-top:0px;display:inline;border-left:0px;border-bottom:0px;" height="239" alt="clip_image004" src="http://sqlblog.com/blogs/ben_nevarez/clip_image004_thumb_5A0DB697.jpg" width="315" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Since the DTA analysis was completed, the needed hypothetical indexes were already dropped. In the next statement I will create the index recommended by the DTA, but instead of a regular index I will create it as a hypothetical index by adding WITH STATISTICS_ONLY.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;create&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;clustered&lt;/span&gt; &lt;span style="color:blue;"&gt;index&lt;/span&gt; cix_ProductID &lt;span style="color:blue;"&gt;on&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail&lt;span style="color:blue;"&gt; &lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;ProductID&lt;span style="color:gray;"&gt;)       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;with&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;statistics_only       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;You can validate that a hypothetical index and statistics were created by running this (notice that the index is defined as hypothetical on the index_description field) &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:maroon;font-family:'Courier New';mso-no-proof:yes;"&gt;sp_helpindex&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:red;"&gt;'dbo.SalesOrderDetail'       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt; &lt;span style="color:blue;"&gt;from&lt;/span&gt; &lt;span style="color:green;"&gt;sys&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:green;"&gt;stats       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;where&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:fuchsia;"&gt;object_id&lt;/span&gt; &lt;span style="color:gray;"&gt;=&lt;/span&gt; &lt;span style="color:fuchsia;"&gt;object_id&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;'dbo.SalesOrderDetail'&lt;/span&gt;&lt;span style="color:gray;"&gt;)       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;However, at the moment I am not aware of a way outside the DTA to ask the Query Optimizer to consider these hypothetical indexes on an estimated execution plan. So I am not able to see where the previous recommended cost is coming from.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Remove the hypothetical index by running this&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;drop&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;index&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail&lt;span style="color:gray;"&gt;.&lt;/span&gt;cix_ProductID      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Implement the DTA recommendation this time as a regular clustered index&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;create&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;clustered&lt;/span&gt; &lt;span style="color:blue;"&gt;index&lt;/span&gt; cix_ProductID &lt;span style="color:blue;"&gt;on&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail&lt;span style="color:blue;"&gt; &lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;ProductID&lt;span style="color:gray;"&gt;)       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;After implementing the recommendation and running the query, the clustered index is in fact being used by the Query Optimizer and this time the estimated cost I got was 0.0033652, very close to the recommended cost listed before on the msdb..DTA_reports_query.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Finally, drop the dbo.SalesOrderDetail table you just created.&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=18767" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Cost+Estimation/default.aspx">Cost Estimation</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Database+Engine+Tuning+Advisor/default.aspx">Database Engine Tuning Advisor</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Hypothetical+Indexes/default.aspx">Hypothetical Indexes</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Query+Optimizer/default.aspx">Query Optimizer</category></item><item><title>Are You Using Scalable Shared Databases?</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2009/10/30/are-you-using-scalable-shared-databases.aspx</link><pubDate>Fri, 30 Oct 2009 07:26:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:18368</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>12</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/18368.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=18368</wfw:commentRss><description>&lt;p&gt;Did you know that you can share read-only databases between several instances of SQL Server?&lt;/p&gt;  &lt;p&gt;Scalable Shared Databases is a very interesting SQL Server feature that many of us seem to almost have forgotten about it. Available for the first time in SQL Server 2005 and originally described on the Microsoft KB article 910378, it was later fully documented on Books Online. This Enterprise edition-only feature allows a read-only database to be accessed at the same time by two or more SQL Server instances (maximum recommended is 8). This configuration offers some performance benefits by allowing each of these instances to use its own resources like memory, CPU, and tempdb database. Scalable Shared Databases are used as reporting databases.&lt;/p&gt;  &lt;p&gt;The concept behind Scalable Shared Databases is very simple: you copy a detached database to a volume, configure that volume as read-only, and then you can attach this database by several SQL Server instances. A shared database will look like a regular read-only database in Management Studio.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://sqlblog.com/blogs/ben_nevarez/clip_image002_203CEA28.jpg"&gt;&lt;img title="clip_image002" style="border-right:0px;border-top:0px;display:block;float:none;margin-left:auto;border-left:0px;margin-right:auto;border-bottom:0px;" height="238" alt="clip_image002" src="http://sqlblog.com/blogs/ben_nevarez/clip_image002_thumb_3BA36F9B.jpg" width="273" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;By the way, it would be interesting to know if Microsoft plans to add additional functionality to this scalability feature or to extend it to read-write databases, perhaps something similar to what Oracle RAC is already doing.&lt;/p&gt;  &lt;p&gt;For more details, especially on restrictions and configuration, see the ‘Deploying a Scalable Shared Database’ entry on Books Online.&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=18368" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Scalability/default.aspx">Scalability</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Scalable+Shared+Databases/default.aspx">Scalable Shared Databases</category></item><item><title>Rebuilding Indexes vs. Updating Statistics</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2009/10/06/rebuilding-indexes-vs-updating-statistics.aspx</link><pubDate>Tue, 06 Oct 2009 07:04:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:17301</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>3</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/17301.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=17301</wfw:commentRss><description>&lt;p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;One of the questions I was asked recently while speaking at user groups, was regarding the order that jobs like rebuilding indexes or updating statistics should be performed as part of the database maintenance activities. Then I started writing this post about this topic on the weekend but was interrupted several times, including one of them to watch the premiere on VH1 of the movie Anvil: The Story of Anvil.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;In general, the order should not matter, at least if you carefully consider these important points:&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;1) By default, the UPDATE STATISTICS statement uses only a sample of records of the table. Using UPDATE STATISTICS WITH FULLSCAN will scan the entire table. &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;2) By default, the UPDATE STATISTICS statement updates both index and column statistics. Using the COLUMNS option will update column statistics only. Using the INDEX option will update index statistics only.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;3) Rebuilding an index, for example by using ALTER INDEX … REBUILD, will also update index statistics with the equivalent of using WITH FULLSCAN. Rebuilding indexes does not update column statistics.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;4) Reorganizing an index, for example using ALTER INDEX … REORGANIZE, does not update any statistics.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;So depending on your maintenance jobs and scripts several scenarios can exist.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;The simplest scenario is if you want to rebuild all the indexes and update all the statistics. As mentioned before, if you rebuild all your indexes then all the index statistics will also be updated by scanning all the rows on the table. Then you just need to update your column statistics by running UPDATE STATISTICS WITH FULLSCAN, COLUMNS. Since the first job only updates index statistics and the second one only updates column statistics, it does not matter which one you execute first.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Some other more complicated scenarios include when you have a job which rebuilds your indexes depending on their fragmentation level. In these cases perhaps you want to update only those index statistics that were not touched by the index rebuild job, plus all the column statistics. &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Of course, the worst case scenario would be if you first rebuild your indexes, which also updates the index statistics by scanning the entire table, and later you run UPDATE STATISTICS using the default values, which again updates the index statistics but this time with a default sample. Not only are you updating your index statistics twice but you are overwriting the better of the two choices.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Let me show you how these commands work with some examples using the AdventureWorks database. Create a new table dbo.SalesOrderDetail&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt; &lt;span style="color:blue;"&gt;into&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;from&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; sales&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;The next query uses the sys.stats catalog view and shows that there are no statistics objects for the new table. &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; name&lt;span style="color:gray;"&gt;,&lt;/span&gt; auto_created&lt;span style="color:gray;"&gt;,&lt;/span&gt; &lt;span style="color:fuchsia;"&gt;stats_date&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:fuchsia;"&gt;object_id&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt; stats_id&lt;span style="color:gray;"&gt;)&lt;/span&gt; &lt;span style="color:blue;"&gt;as&lt;/span&gt; update_date &lt;span style="color:blue;"&gt;from&lt;/span&gt; &lt;span style="color:green;"&gt;sys&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:green;"&gt;stats&lt;/span&gt;       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;where&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:fuchsia;"&gt;object_id&lt;/span&gt; &lt;span style="color:gray;"&gt;=&lt;/span&gt; &lt;span style="color:fuchsia;"&gt;object_id&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;'dbo.SalesOrderDetail'&lt;/span&gt;&lt;span style="color:gray;"&gt;)       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Use this query again to inspect the status of the statistics after each of the following commands. Now run the following query&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt; &lt;span style="color:blue;"&gt;from&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;where&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; SalesOrderID &lt;span style="color:gray;"&gt;=&lt;/span&gt; 43670 &lt;span style="color:gray;"&gt;and&lt;/span&gt; OrderQty &lt;span style="color:gray;"&gt;=&lt;/span&gt; 1      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Use the previous sys.stats query to verify that two statistics objects were created, one for the SalesOrderID column and another one for the OrderQty column (they both have names starting with _WA_Sys as shown in the next figure). Now create the following index and again run the query to verify that a new statistics object for the ProductID column has been created. Notice the value of the auto_created column which tells if the statistics were created by the query optimizer.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;create&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;index&lt;/span&gt; ix_ProductID &lt;span style="color:blue;"&gt;on&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail&lt;span style="color:gray;"&gt;(&lt;/span&gt;ProductID&lt;span style="color:gray;"&gt;)&lt;/span&gt;       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="text-align:center;" align="center"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="text-align:center;" align="center"&gt;&lt;a href="http://sqlblog.com/blogs/ben_nevarez/clip_image002_4C71EA23.jpg"&gt;&lt;img title="clip_image002" style="border-right:0px;border-top:0px;display:inline;border-left:0px;border-bottom:0px;" height="78" alt="clip_image002" src="http://sqlblog.com/blogs/ben_nevarez/clip_image002_thumb_5A725C90.jpg" width="399" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Run the next command to update the column statistics only. You can validate that only the column statistics were updated by looking at the update_date column which uses the STATS_DATE function to display the last date the statistics were updated. &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;update&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;statistics&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail &lt;span style="color:blue;"&gt;with&lt;/span&gt; &lt;span style="color:blue;"&gt;fullscan&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt; &lt;span style="color:green;"&gt;columns       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="text-align:center;mso-layout-grid-align:none;" align="center"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;&lt;a href="http://sqlblog.com/blogs/ben_nevarez/clip_image004_53814C8A.jpg"&gt;&lt;img title="clip_image004" style="border-right:0px;border-top:0px;display:inline;border-left:0px;border-bottom:0px;" height="79" alt="clip_image004" src="http://sqlblog.com/blogs/ben_nevarez/clip_image004_thumb_286AE1F5.jpg" width="406" border="0" /&gt;&lt;/a&gt;      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;This command will do the same for the index statistics&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;update&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;statistics&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail &lt;span style="color:blue;"&gt;with&lt;/span&gt; &lt;span style="color:blue;"&gt;fullscan&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt; &lt;span style="color:blue;"&gt;index       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;These commands will update both index and column statistics&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;update&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;statistics&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail &lt;span style="color:blue;"&gt;with&lt;/span&gt; &lt;span style="color:blue;"&gt;fullscan       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;update&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;statistics&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail &lt;span style="color:blue;"&gt;with&lt;/span&gt; &lt;span style="color:blue;"&gt;fullscan&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt; &lt;span style="color:gray;"&gt;all       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:gray;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;See how an index rebuild only updates index statistics&lt;span style="font-size:10pt;color:gray;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:gray;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;index&lt;/span&gt; ix_ProductID &lt;span style="color:blue;"&gt;on&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail &lt;span style="color:blue;"&gt;rebuild       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;Here you can verify that reorganizing an index does not update statistics&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;index&lt;/span&gt; ix_ProductID&lt;span style="mso-spacerun:yes;"&gt;&amp;#160; &lt;/span&gt;&lt;span style="color:blue;"&gt;on&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail &lt;span style="color:blue;"&gt;reorganize       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;Finally, remove the table you have just created&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;drop&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;table&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=17301" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Indexes/default.aspx">Indexes</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Statistics/default.aspx">Statistics</category></item><item><title>The Missing Indexes Feature</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2009/09/25/the-missing-indexes-feature.aspx</link><pubDate>Fri, 25 Sep 2009 06:19:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:16984</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>3</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/16984.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=16984</wfw:commentRss><description>&lt;p class="MsoNormal"&gt;Since I will be speaking about the Query Optimizer at the coming PASS Summit, I have been preparing my presentation and at the same time blogging about it. This time I will describe the Missing Indexes feature, seen from the point of view of the Query Optimizer.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;We know that it is the job of the Query Optimizer to find an efficient execution plan for a query. But we rarely see the Query Optimizer directly giving us indications about what it needs to produce a better execution plan. One of these cases is the Missing Indexes feature, which was introduced with SQL Server 2005.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;The Query Optimizer defines what the best indexes for a query are, and if these indexes do not exist, it will make this information available in the XML plan and the sys.dm_db_missing_index DMVs. And of course, by showing this information the Query Optimizer is also warning you that it might not be selecting an efficient plan. This information shows which indexes may be helpful to improve the performance of your query. You can even use SQL Server 2008 Management Studio to display the CREATE INDEX commands needed to create these indexes, as shown later.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;However, although this information about missing indexes is very helpful, this feature should not be used as a tuning tool and should not replace your own index analysis. Database administrators and developers should be aware of its limitations, as described on the Books Online entry ‘Limitations of the Missing Indexes Feature’. &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;So let us take a quick look to see how this feature works. Create a dbo.SalesOrderDetail table on the AdventureWorks database with the following command&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt;       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;into&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;from&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; sales&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Run this query and ask for a graphical or XML execution plan&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt;       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;from&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;where&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; SalesOrderID &lt;span style="color:gray;"&gt;=&lt;/span&gt; 43670      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;This query can benefit from an index on the SalesOrderID column but no missing indexes information is shown this time. One limitation of the Missing Indexes feature is that it does not work on a trivial plan optimization, like in this case. You can verify that this is a trivial plan by looking at the graphical plan properties (Optimization Level shows as TRIVIAL) or by looking at the XML plan (StatementOptmLevel=&amp;quot;TRIVIAL).&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;You can avoid the trivial plan optimization by using more complex features. In our case we are just going to create a non related index&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;create&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;index&lt;/span&gt; ix_ProductID &lt;span style="color:blue;"&gt;on&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail&lt;span style="color:gray;"&gt;(&lt;/span&gt;ProductID&lt;span style="color:gray;"&gt;)       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Note that the index created will not be used by our previous query but the query will no longer qualify for a trivial plan. Run the query again. This time the XML plan will contain something like this&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;MissingIndexes&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;gt;     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&lt;span style="mso-spacerun:yes;"&gt;&amp;#160;&amp;#160; &lt;/span&gt;&amp;lt;&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;MissingIndexGroup&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;/span&gt;&lt;span style="font-size:10pt;color:red;font-family:'Courier New';mso-no-proof:yes;"&gt;Impact&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;=&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;quot;&lt;span style="color:blue;"&gt;99.703&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt;&amp;gt;       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&lt;span style="mso-spacerun:yes;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/span&gt;&amp;lt;&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;MissingIndex&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;/span&gt;&lt;span style="font-size:10pt;color:red;font-family:'Courier New';mso-no-proof:yes;"&gt;Database&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;=&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;quot;&lt;span style="color:blue;"&gt;[AdventureWorks]&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt; &lt;/span&gt;&lt;span style="color:red;"&gt;Schema&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt;[dbo]&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt; &lt;/span&gt;&lt;span style="color:red;"&gt;Table&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt;[SalesOrderDetail]&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt;&amp;gt;       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&lt;span style="mso-spacerun:yes;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/span&gt;&amp;lt;&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;ColumnGroup&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;/span&gt;&lt;span style="font-size:10pt;color:red;font-family:'Courier New';mso-no-proof:yes;"&gt;Usage&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;=&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;quot;&lt;span style="color:blue;"&gt;EQUALITY&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt;&amp;gt;       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&lt;span style="mso-spacerun:yes;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/span&gt;&amp;lt;&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;Column&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;/span&gt;&lt;span style="font-size:10pt;color:red;font-family:'Courier New';mso-no-proof:yes;"&gt;Name&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;=&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;quot;&lt;span style="color:blue;"&gt;[SalesOrderID]&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt; &lt;/span&gt;&lt;span style="color:red;"&gt;ColumnId&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt;1&lt;/span&gt;&amp;quot;&lt;span style="color:blue;"&gt; /&amp;gt;       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&lt;span style="mso-spacerun:yes;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/span&gt;&amp;lt;/&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;ColumnGroup&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;gt;     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&lt;span style="mso-spacerun:yes;"&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/span&gt;&amp;lt;/&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;MissingIndex&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;gt;     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&lt;span style="mso-spacerun:yes;"&gt;&amp;#160;&amp;#160; &lt;/span&gt;&amp;lt;/&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;MissingIndexGroup&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;gt;     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="font-size:10pt;color:#a31515;font-family:'Courier New';mso-no-proof:yes;"&gt;MissingIndexes&lt;/span&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;&amp;gt;     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;And if you look at the graphical plan (only SQL Server 2008 Management Studio) you will see a Missing Index warning and a CREATE INDEX command&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="text-align:center;" align="center"&gt;&lt;a href="http://sqlblog.com/blogs/ben_nevarez/clip_image002_62264090.jpg"&gt;&lt;img title="clip_image002" style="border-right:0px;border-top:0px;display:inline;border-left:0px;border-bottom:0px;" height="148" alt="clip_image002" src="http://sqlblog.com/blogs/ben_nevarez/clip_image002_thumb_0FD58CC6.jpg" width="514" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;You can right-click on the graphical plan and select Missing Index Details to see the CREATE INDEX command that can be used to create this index&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;/*     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;Missing Index Details from SQLQuery1.sql     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;The Query Processor estimates that implementing the following index could improve the query cost by 99.703%.     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;*/     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;/*     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;USE [AdventureWorks]     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;GO     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;CREATE NONCLUSTERED INDEX [&amp;lt;Name of Missing Index, sysname,&amp;gt;]     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;ON [dbo].[SalesOrderDetail] ([SalesOrderID])     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;     &lt;p&gt;&amp;#160;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;GO     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:green;font-family:'Courier New';mso-no-proof:yes;"&gt;*/     &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Create the recommended index after you provide a name to it. This time if you run the same query again and look at the execution plan you will see that an Index Seek operator is using the index you have just created and both the Missing Index warning and the MissingIndex element of the XML plan are gone.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Finally, remove the dbo.SalesOrderDetail table you have just created.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;drop&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;table&lt;/span&gt; dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=16984" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Indexes/default.aspx">Indexes</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Optimization/default.aspx">Optimization</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Query+Optimizer/default.aspx">Query Optimizer</category></item><item><title>How the Query Optimizer Uses Statistics</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2009/09/03/how-the-query-optimizer-uses-statistics.aspx</link><pubDate>Thu, 03 Sep 2009 06:00:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:16548</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>4</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/16548.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=16548</wfw:commentRss><description>&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;This post shows how the Query Optimizer uses statistics to estimate the selectivity of expressions during query optimization.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;You can also use this as a second part of my last post, The Query Optimizer and Parameter Sniffing. Here I will show you the advantage of the use of statistics when the Query Optimizer can “sniff” the parameter values compared to just guessing the selectivity of expressions when local variables are used. So I will be using the same query as in that previous post.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Notice that there are many other details that can not be covered here so this post will focus on a very simple example to show how the Query Optimizer creates and uses statistics.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;To start open your AdventureWorks database and run this to display the current statistics on the ProductID column of the Sales.SalesOrderDetail table&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;dbcc&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;show_statistics&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;'Sales.SalesOrderDetail'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; IX_SalesOrderDetail_ProductID&lt;SPAN style="COLOR:gray;"&gt;) &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;This will display the header, density vector and histogram of the statistics object.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;1) Understanding the Histogram&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;First I will explain the meaning of the values of a histogram’s steps. Let us take a look at step 86, shown here&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image002_59D696CA.jpg"&gt;&lt;IMG title=clip_image002 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:block;FLOAT:none;MARGIN-LEFT:auto;BORDER-LEFT:0px;MARGIN-RIGHT:auto;BORDER-BOTTOM:0px;" height=73 alt=clip_image002 src="http://sqlblog.com/blogs/ben_nevarez/clip_image002_thumb_5E0F110E.jpg" width=505 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;RANGE_HI_KEY is the upper boundary of a histogram step. The value 826 is the upper boundary for step 85 and 831 is the upper boundary for step 86. This means that step 86 may contain only values from 827 to 831.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Run the following query to obtain the real number of records for ProductIDs 827 to 831 to compare them against the histogram&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; &lt;SPAN style="COLOR:fuchsia;"&gt;COUNT&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(*)&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;as&lt;/SPAN&gt; Total &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;from&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; Sales&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;SalesOrderDetail &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;SPAN style="COLOR:gray;"&gt;between&lt;/SPAN&gt; 827 &lt;SPAN style="COLOR:gray;"&gt;and&lt;/SPAN&gt; 831 &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;group&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;by&lt;/SPAN&gt; ProductID &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;This produces the following result&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image004_77390DC5.jpg"&gt;&lt;IMG title=clip_image004 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:block;FLOAT:none;MARGIN-LEFT:auto;BORDER-LEFT:0px;MARGIN-RIGHT:auto;BORDER-BOTTOM:0px;" height=107 alt=clip_image004 src="http://sqlblog.com/blogs/ben_nevarez/clip_image004_thumb_658AA66A.jpg" width=147 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Going back to the histogram, EQ_ROWS is the estimated number of rows whose column value equals RANGE_HI_KEY. In our example RANGE_HI_KEY is 831 and the number of records with ProductID 831 is 198. The same value is shown on the histogram.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;RANGE_ROWS is the estimated number of rows whose column value falls inside the range of the step, excluding the upper boundary. In our example, this is the number of records with values from 827 to 830 (831, the upper boundary, is excluded). The histogram shows 110 records and we could obtain the same value by getting the sum of 31 records for 827, 46 records for 828, 0 records for 829 and 33 records for 830. &lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;DISTINCT_RANGE_ROWS is the estimated number of rows with a distinct column value inside this range, excluding the upper bound. In our example we have records for three distinct values: 827, 828, and 830, so DISTINCT_RANGE_ROWS is 3. There are no records for ProductID 829 and 831 is again excluded.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Finally, AVG_RANGE_ROWS is the average number of rows per distinct value and it is calculated as RANGE_ROWS / DISTINCT_RANGE_ROWS. In our example, we have a total of 110 records for 3 DISTINCT_RANGE_ROWS, so that gives, 110 / 3 = 36.6667 also shown on the histogram for step 86.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Now let us see how the statistics are used to estimate the selectivity of the queries.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;2) When the Query Optimizer knows the value&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Let us see the first query&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; Sales&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;SalesOrderDetail &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 831 &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;mso-layout-grid-align:none;" align=center&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image006_29F93A29.jpg"&gt;&lt;IMG title=clip_image006 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:inline;BORDER-LEFT:0px;BORDER-BOTTOM:0px;" height=140 alt=clip_image006 src="http://sqlblog.com/blogs/ben_nevarez/clip_image006_thumb_782FC610.jpg" width=370 border=0&gt;&lt;/A&gt; 
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal&gt;Since 831 is a RANGE_HI_KEY on step 86, the Query Optimizer will use the EQ_ROWS value and the estimated number of rows will be 198.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Now run the same query with the value 828&lt;/P&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image008_713EB60A.jpg"&gt;&lt;IMG title=clip_image008 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:inline;BORDER-LEFT:0px;BORDER-BOTTOM:0px;" height=143 alt=clip_image008 src="http://sqlblog.com/blogs/ben_nevarez/clip_image008_thumb_716CE27C.jpg" width=374 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;This time the value is inside the range of step 86 but it is not a RANGE_HI_KEY so the Query Optimizer uses the value calculated before as AVG_RANGE_ROWS. Actually, we get the same estimated number of rows for any of the other values in the range (except RANGE_HI_KEY). This also includes 829, even when there are no records for this ProductID.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Let us try now a nonequality operator and try to find the number of records less than 714. For these we need to calculate the sum of the values of both RANGE_ROWS and EQ_ROWS for steps 1 thru 7, which give us a total of 13,223 rows.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image010_1869252F.jpg"&gt;&lt;IMG title=clip_image010 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:inline;BORDER-LEFT:0px;BORDER-BOTTOM:0px;" height=178 alt=clip_image010 src="http://sqlblog.com/blogs/ben_nevarez/clip_image010_thumb_6CE687A4.jpg" width=522 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;This is the query and the estimated number of rows is shown on the execution plan&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; Sales&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;SalesOrderDetail &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;SPAN style="COLOR:gray;"&gt;&amp;lt;&lt;/SPAN&gt; 714 &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image012_05A45167.jpg"&gt;&lt;IMG title=clip_image012 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:inline;BORDER-LEFT:0px;BORDER-BOTTOM:0px;" height=147 alt=clip_image012 src="http://sqlblog.com/blogs/ben_nevarez/clip_image012_thumb_1ECE4E1E.jpg" width=340 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;3) When the Query Optimizer does not know the value&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;In the case when the Query Optimizer does not know the value used in the expression, like when local variables are used, the Query Optimizer can not use the histogram so it will use some other information including the statistics density information or it will try to guess the selectivity using some fixed percentages. First, let us try using the equality operator.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;declare&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; @pid &lt;SPAN style="COLOR:blue;"&gt;int&lt;/SPAN&gt; &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 897 &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; Sales&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;SalesOrderDetail &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; @pid&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The Query Optimizer is not able to see the value 897 in this query. As explained in my previous post, the Query Optimizer does not know the value of the @pid local variable at optimization time. So it will use the density value of the ProductID column, 0.003759399, as listed on the second section, density vector, of the DBCC SHOW_STATISTICS output. If we multiply this value by the total number of records, 121,317, we will get 456.079 which will be shown in the next execution plan.&lt;/P&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image014_5EC66115.jpg"&gt;&lt;IMG title=clip_image014 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:inline;BORDER-LEFT:0px;BORDER-BOTTOM:0px;" height=156 alt=clip_image014 src="http://sqlblog.com/blogs/ben_nevarez/clip_image014_thumb_3ACF32F8.jpg" width=344 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Finally, run this query with a nonequality operator&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;declare&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; @pid &lt;SPAN style="COLOR:blue;"&gt;int&lt;/SPAN&gt; &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 897 &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; Sales&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;SalesOrderDetail &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;SPAN style="COLOR:gray;"&gt;&amp;lt;&lt;/SPAN&gt; @pid &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal&gt;Same as before, the value 897 does not matter; any other value will give you the same estimated number of rows and execution plan. The estimated number of rows is always 30% of the total number of records for a nonequality operator. In this case the 30% of 121,317 is 36,395.1 as shown next.&lt;/P&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image016_6CF4FFF4.jpg"&gt;&lt;IMG title=clip_image016 style="BORDER-RIGHT:0px;BORDER-TOP:0px;DISPLAY:inline;BORDER-LEFT:0px;BORDER-BOTTOM:0px;" height=161 alt=clip_image016 src="http://sqlblog.com/blogs/ben_nevarez/clip_image016_thumb_340C4F64.jpg" width=352 border=0&gt;&lt;/A&gt;&lt;/P&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=16548" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Optimization/default.aspx">Optimization</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Query+Optimizer/default.aspx">Query Optimizer</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Statistics/default.aspx">Statistics</category></item><item><title>The Query Optimizer and Parameter Sniffing</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2009/08/27/the-query-optimizer-and-parameter-sniffing.aspx</link><pubDate>Fri, 28 Aug 2009 02:42:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:16347</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>7</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/16347.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=16347</wfw:commentRss><description>&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;As we all know, the SQL Server Query Optimizer uses statistics to estimate the cardinality and selectivity of predicates of a query to produce an optimal execution plan. The Query Optimizer accomplishes this by first inspecting the values of the query parameters. This behavior is called parameter sniffing and it is a good thing. Getting an execution plan tailored to the current parameters of a query improves the performance of your applications.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;We also know that the plan cache can store these execution plans so they can be reused the next time the same query needs to be executed again. This saves optimization time and CPU resources as the query does not need to be compiled again.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;However, although the Query Optimizer and the plan cache work fine together most of the times, occasionally some performance problems can appear. Since the Query Optimizer may produce different execution plans for the same query, depending on its parameters, caching and reusing only one of these plans may be a performance issue for some other instance of this query that could benefit from a better plan.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;This is a known problem with queries using explicit parameterization like in, for example, stored procedures. So let us see an example of what the problem is and a few recommendations on how to fix it.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;1) The problem&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Let us write a simple stored procedure using the Sales.SalesOrderDetail table on the AdventureWorks database&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;create&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;procedure&lt;/SPAN&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:blue;"&gt;int&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;) 
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;as &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; Sales&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;SalesOrderDetail &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; @pid &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;Run this to display the amount of disk activity generated by the query&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;set&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;statistics&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;io&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;on 
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;and execute the stored procedure&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;exec&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 897 
&lt;P&gt;&lt;/SPAN&gt;The Query Optimizer estimates that only a few records will be returned by this query and decides to use an existing index, so if you look at the actual execution plan you will see an Index Seek and a Key Lookup operators. The I/O information will be similar to this&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:8pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;logical reads 10, physical reads 0, read-ahead reads 0&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:8pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Now clear the plan cache to remove this plan and run the stored procedure again using a new parameter (Note: Be careful not to clear the plan cache of a production environment)&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;dbcc&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; freeproccache 
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;exec&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 870 &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;This time you will get 4,688 rows and the execution plan will show a Clustered Index Scan. The I/O information will be similar to this&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:8pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;logical reads 1240, physical reads 0, read-ahead reads 0&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:8pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;/SPAN&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;In this example each execution created its own optimal execution plan. Now see what happen when the plan cache is not cleared before the second execution so they both use the same cached plan&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;dbcc&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; freeproccache 
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;exec&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 870 &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;exec&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 897 &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;This time the Query Optimizer will compile the first execution of the stored procedure and will create an optimal execution plan for the value 870. This will use a Clustered Index Scan and around 1,240 logical reads. Since this plan is cached, it will also be used for the second execution, using the value 897 and it will also show a Clustered Index Scan and around 1,240 logical reads. This second execution is using 124 times more reads than its optimal plan, as shown previously.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;You can try the other combination as well, clearing the plan cache, and running these two executions of the stored procedure, but this time using the value 897 on the first one and 870 on the second one.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;2) Optimize for a typical parameter&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;There might be cases when most of the executions of a stored procedure use the same execution plan and/or you want to avoid the optimization cost. For these cases you can use the OPTIMIZE FOR hint. Use this hint when an optimal plan is generated for the majority of values used for the parameter. Only the few executions using an atypical parameter will not have an optimal plan.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Suppose that almost all of the executions of our stored procedure would benefit from the previous plan using an Index Seek and a Key Lookup.&lt;SPAN style="mso-spacerun:yes;"&gt;&amp;nbsp; &lt;/SPAN&gt;You could write the stored procedure this way&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;procedure&lt;/SPAN&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:blue;"&gt;int&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;) 
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;as &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; Sales&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;SalesOrderDetail &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; @pid &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;option &lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:gray;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;optimize &lt;SPAN style="COLOR:blue;"&gt;for &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 897&lt;SPAN style="COLOR:gray;"&gt;)) &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;When you run the stored procedure it will be optimized for the value 897, no matter what parameter value was specified for the execution. Test the following case&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;exec&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 870 
&lt;P&gt;&lt;/SPAN&gt;You can find this in the XML plan&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:#a31515;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;ParameterList&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&amp;gt; 
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:#a31515;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;ColumnReference&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:red;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;Column&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;=&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;"&lt;SPAN style="COLOR:blue;"&gt;@pid&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;ParameterCompiledValue&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt;(897)&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;ParameterRuntimeValue&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt;(870)&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt; /&amp;gt; &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:#a31515;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;ParameterList&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&amp;gt; &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;This clearly shows which value was used during optimization and which one during execution. In this case the stored procedure is optimized only once, and the plan is stored on the plan cache and reused as many times as needed. The benefit of using this hint is that you have total control on which plan is stored on the plan cache.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;3) Optimize on every execution&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;If you want the best performance for every query the solution might be to optimize for every execution. You will get an optimal plan on every execution but will pay for the optimization cost. To do this use the RECOMPILE hint as shown here.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;procedure&lt;/SPAN&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:blue;"&gt;int&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;) 
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;as &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; Sales&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;SalesOrderDetail &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; ProductID &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; @pid &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;option &lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:gray;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;recompile&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:gray;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;) &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;The XML plan for this execution&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;exec&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; test&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;@pid &lt;SPAN style="COLOR:gray;"&gt;=&lt;/SPAN&gt; 897 
&lt;P&gt;&lt;/SPAN&gt;will show&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="mso-layout-grid-align:none;"&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:#a31515;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;ParameterList&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&amp;gt; 
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:#a31515;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;ColumnReference&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:red;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;Column&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;=&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;"&lt;SPAN style="COLOR:blue;"&gt;@pid&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;ParameterCompiledValue&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt;(897)&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;ParameterRuntimeValue&lt;/SPAN&gt;&lt;SPAN style="COLOR:blue;"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt;(897)&lt;/SPAN&gt;"&lt;SPAN style="COLOR:blue;"&gt; /&amp;gt; &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:#a31515;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;ParameterList&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;&amp;gt; &lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;Some other solution that has been traditionally implemented before has been using local variables but usually this is not a good idea. By doing this you are not only disabling parameter sniffing but also disabling the choice of the Query Optimizer to use the statistics histogram&amp;nbsp;to find an optimal plan for the query. This solution will use the same execution plan for all the executions but may not be the optimal plan for any of them.&lt;/P&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=16347" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Optimization/default.aspx">Optimization</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Parameter+Sniffing/default.aspx">Parameter Sniffing</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Query+Optimizer/default.aspx">Query Optimizer</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Statistics/default.aspx">Statistics</category></item><item><title>The Query Optimizer and Computed Columns</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2009/08/10/the-query-optimizer-and-computed-columns.aspx</link><pubDate>Mon, 10 Aug 2009 06:39:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:15918</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>0</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/15918.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=15918</wfw:commentRss><description>&lt;p class="MsoNormal"&gt;In my previous post I talked about contradiction detection as one of the early steps in query optimization. Another interesting step performed during query optimization is the automatic matching of computed columns. Although computed columns have been available in previous versions of SQL Server, the automatic matching feature was introduced until SQL Server 2005. In this post I will show you how this feature works and explain how computed columns can help you improve the performance of your queries.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;A problem faced by queries using scalar expressions is that they usually can not take benefit of column statistics. Statistics provide cardinality estimation to help the Query Optimizer to create better execution plans. Without statistics, the Query Optimizer will use a 30% selectivity on inequality comparisons which may produce inefficient execution plans.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;A solution to this problem is the use of computed columns as SQL Server can automatically create and update statistics on these columns. The great benefit of this solution is that you do not need to specify the name of the computed column in your queries for SQL Server to use its statistics. The Query Optimizer automatically matches the computed column definition to an existing scalar expression in a query, so your applications do not need to be changed.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Indexes can also be created on computed columns but be aware of the requirements described on the Books Online entry ‘Creating Indexes on Computed Columns’.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;I will show you one example; run this query against the AdventureWorks database&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt; &lt;span style="color:blue;"&gt;from&lt;/span&gt; Sales&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;where&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; OrderQty &lt;span style="color:gray;"&gt;*&lt;/span&gt; UnitPrice &lt;span style="color:gray;"&gt;&amp;gt;&lt;/span&gt; 10000      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="text-align:center;" align="center"&gt;&lt;a href="http://sqlblog.com/blogs/ben_nevarez/clip_image002_7FD0B9E4.jpg"&gt;&lt;img title="clip_image002" style="border-right:0px;border-top:0px;display:inline;border-left:0px;border-bottom:0px;" height="179" alt="clip_image002" src="http://sqlblog.com/blogs/ben_nevarez/clip_image002_thumb_3B524C15.jpg" width="343" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;The estimated number of rows is 36,395.1, which is 30% of the total number of rows, 121,317. But the query only returns 772 records.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Now create a computed column &lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;table&lt;/span&gt; Sales&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;add&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; cc &lt;span style="color:blue;"&gt;as&lt;/span&gt; OrderQty &lt;span style="color:gray;"&gt;*&lt;/span&gt; UnitPrice      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Run the previous query again. Note that this time the estimated number of rows has changed and it is close to the actual number of rows returned by the query. Replace the 10000 with some other values like 10, 100, 1000 and 5000 and compare the actual and the estimated number of rows.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="text-align:center;" align="center"&gt;&lt;a href="http://sqlblog.com/blogs/ben_nevarez/clip_image004_48E68B8D.jpg"&gt;&lt;img title="clip_image004" style="border-right:0px;border-top:0px;display:inline;border-left:0px;border-bottom:0px;" height="222" alt="clip_image004" src="http://sqlblog.com/blogs/ben_nevarez/clip_image004_thumb_2B3633FE.jpg" width="336" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;Note that creating the computed column does not create statistics. These statistics are created only until the query is executed. You can run this to see information about its statistics, which name starts with _WA_Sys_.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt; &lt;span style="color:blue;"&gt;from&lt;/span&gt; &lt;span style="color:green;"&gt;sys&lt;/span&gt;&lt;span style="color:gray;"&gt;.&lt;/span&gt;&lt;span style="color:green;"&gt;stats       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;where&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:fuchsia;"&gt;object_id&lt;/span&gt; &lt;span style="color:gray;"&gt;=&lt;/span&gt; &lt;span style="color:fuchsia;"&gt;object_id&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;'Sales.SalesOrderDetail'&lt;/span&gt;&lt;span style="color:gray;"&gt;)       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Also note that the auto_created field is 1 which means that the statistics were automatically created by SQL Server. Use the following command to display these statistics&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;dbcc&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;show_statistics &lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;'Sales.SalesOrderDetail'&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt; _WA_Sys_00000013_2645B050&lt;span style="color:gray;"&gt;)       &lt;p&gt;&lt;/p&gt;     &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Unfortunately, for automatic matching to work the expression must be exactly the same, if I change the query to UnitPrice * OrderQty, instead of OrderQty * UnitPrice, it will show an estimated number of rows of 30% again.&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;select&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:gray;"&gt;*&lt;/span&gt; &lt;span style="color:blue;"&gt;from&lt;/span&gt; Sales&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;where&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; UnitPrice &lt;span style="color:gray;"&gt;*&lt;/span&gt; OrderQty &lt;span style="color:gray;"&gt;&amp;gt;&lt;/span&gt; 10000      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Finally, drop the created computed column&lt;/p&gt;  &lt;p class="MsoNormal"&gt;   &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;table&lt;/span&gt; Sales&lt;span style="color:gray;"&gt;.&lt;/span&gt;SalesOrderDetail       &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal" style="mso-layout-grid-align:none;"&gt;&lt;span style="font-size:10pt;color:blue;font-family:'Courier New';mso-no-proof:yes;"&gt;drop&lt;/span&gt;&lt;span style="font-size:10pt;font-family:'Courier New';mso-no-proof:yes;"&gt; &lt;span style="color:blue;"&gt;column&lt;/span&gt; cc      &lt;p&gt;&lt;/p&gt;   &lt;/span&gt;&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=15918" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Computed+Columns/default.aspx">Computed Columns</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Optimization/default.aspx">Optimization</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Query+Optimizer/default.aspx">Query Optimizer</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Statistics/default.aspx">Statistics</category></item><item><title>The Query Optimizer and Contradiction Detection</title><link>http://sqlblog.com/blogs/ben_nevarez/archive/2009/08/04/the-query-optimizer-and-contradiction-detection.aspx</link><pubDate>Tue, 04 Aug 2009 06:56:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:15733</guid><dc:creator>Ben Nevarez</dc:creator><slash:comments>7</slash:comments><comments>http://sqlblog.com/blogs/ben_nevarez/comments/15733.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/ben_nevarez/commentrss.aspx?PostID=15733</wfw:commentRss><description>&lt;P class=MsoNormal&gt;One of the many interesting topics I found while working as technical editor of the SQL Server 2008 Internals book was the concept of contradiction detection. So, in this post I will to try to expand on this topic and provide some additional examples.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Contradiction detection is performed by SQL Server during the early steps of the query optimization process. During this process the Query Optimizer tries to find contradictions that can be removed and can make the query perform better. Since these parts of the query are not executed at all, SQL Server saves resources like I/O, locks, memory and CPU, making the query to be executed faster. For example, the Query Optimizer may know that no records can satisfy a predicate even before touching any page of data.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;A contradiction may be related to a check constraint, or may be related to the way the query is written, that is, the query itself contains a contradiction. Let us explore both scenarios and analyze the execution plans that are generated on each case.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;First, I need to a table with a check constraint. The following code creates a table that stores information about classic rock albums and its check constraint validates that every record entered was released before 1990.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:green;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;-- create table &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;create&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;table&lt;/SPAN&gt; albums&lt;SPAN style="COLOR:blue;"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;( &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;name &lt;SPAN style="COLOR:blue;"&gt;varchar&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;40&lt;SPAN style="COLOR:gray;"&gt;),&lt;/SPAN&gt; &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;band &lt;SPAN style="COLOR:blue;"&gt;varchar&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;40&lt;SPAN style="COLOR:gray;"&gt;),&lt;/SPAN&gt; &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;album_year &lt;SPAN style="COLOR:blue;"&gt;smallint&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;, &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;constraint&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; check_year &lt;SPAN style="COLOR:blue;"&gt;check &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;album_year &lt;SPAN style="COLOR:gray;"&gt;&amp;lt;&lt;/SPAN&gt; 1990&lt;SPAN style="COLOR:gray;"&gt;)) &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:green;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;-- add some records &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;insert&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;into&lt;/SPAN&gt; albums &lt;SPAN style="COLOR:blue;"&gt;values &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;'Machine Head'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; &lt;SPAN style="COLOR:red;"&gt;'Deep Purple'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; 1972&lt;SPAN style="COLOR:gray;"&gt;) &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;insert&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;into&lt;/SPAN&gt; albums &lt;SPAN style="COLOR:blue;"&gt;values &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;'A Night at the Opera'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; &lt;SPAN style="COLOR:red;"&gt;'Queen'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; 1975&lt;SPAN style="COLOR:gray;"&gt;) &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;insert&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;into&lt;/SPAN&gt; albums &lt;SPAN style="COLOR:blue;"&gt;values &lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="COLOR:red;"&gt;'British Steel'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; &lt;SPAN style="COLOR:red;"&gt;'Judas Priest'&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; 1980&lt;SPAN style="COLOR:gray;"&gt;) &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;So, if I run a query to list all the records released, let us say before 1980, the execution plan uses a Table Scan operator, which is exactly what I expected for this scenario.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; albums &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; album_year &lt;SPAN style="COLOR:gray;"&gt;&amp;lt;&lt;/SPAN&gt; 1980 &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image002_34F25E56.jpg"&gt;&lt;IMG title=clip_image002 style="BORDER-TOP-WIDTH:0px;DISPLAY:block;BORDER-LEFT-WIDTH:0px;FLOAT:none;BORDER-BOTTOM-WIDTH:0px;MARGIN-LEFT:auto;MARGIN-RIGHT:auto;BORDER-RIGHT-WIDTH:0px;" height=117 alt=clip_image002 src="http://sqlblog.com/blogs/ben_nevarez/clip_image002_thumb_4769518A.jpg" width=271 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;B style="mso-bidi-font-weight:normal;"&gt;Check Constraint &lt;/B&gt;
&lt;P&gt;But what about if somebody tries to request all the records released, for example, after 1995, like in the following query? Does the Query Optimizer know that no records will be returned even before looking at the data on the table?&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; albums &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; album_year &lt;SPAN style="COLOR:gray;"&gt;&amp;gt;&lt;/SPAN&gt; 1995 &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;In this case the Query Optimizer should know that because of the existing check constraint the previous query returns no records. So if I execute this query there is no need for SQL Server to access the data on the table, right? But surprisingly, the resulting execution plan still shows the Table Scan operator. Why?&lt;/P&gt;
&lt;P class=MsoNormal&gt;Actually, there is another optimization called autoparameterization, which is closely related to the&amp;nbsp;trivial plan feature and&amp;nbsp;that&amp;nbsp;overrides contradiction detection. To avoid this&amp;nbsp;optimization I can use some features like query hints, joins, etc. like in the following example&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; albums &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; album_year &lt;SPAN style="COLOR:gray;"&gt;&amp;gt;&lt;/SPAN&gt; 1995 &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;option &lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:gray;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;maxdop&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; 1&lt;SPAN style="COLOR:gray;"&gt;) &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;Once I change the query to use a query hint,&amp;nbsp;the execution plan does not access the table at all, and instead it is just using a Constant Scan operator. &lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;A href="http://sqlblog.com/blogs/ben_nevarez/clip_image004_2BF2F206.jpg"&gt;&lt;IMG title=clip_image004 style="BORDER-TOP-WIDTH:0px;DISPLAY:block;BORDER-LEFT-WIDTH:0px;FLOAT:none;BORDER-BOTTOM-WIDTH:0px;MARGIN-LEFT:auto;MARGIN-RIGHT:auto;BORDER-RIGHT-WIDTH:0px;" height=96 alt=clip_image004 src="http://sqlblog.com/blogs/ben_nevarez/clip_image004_thumb_2C8D516D.jpg" width=270 border=0&gt;&lt;/A&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Now, see what happens if I disable the check constraint&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:green;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;-- disable constraint &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;table&lt;/SPAN&gt; albums &lt;SPAN style="COLOR:blue;"&gt;nocheck&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;constraint&lt;/SPAN&gt; check_year &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;This time running the last query uses a Table Scan operator again as the optimizer can not longer use the check constraint.&lt;/P&gt;
&lt;P class=MsoNormal&gt;Finally, there are two choices to enable back the existing check constraint. If I ask SQL Server to enable the constraint and validate the existing data, the constraint will be enabled only if all the data complies with the check constraint definition. If I ask not to validate the existing data, the check constraint will be enabled but only the new records will be validated.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Of course the resulting execution plan of my query will be different depending on the selected choice. If the check constraint is enabled validating the existing data, running the same query again (and including the query hint) will use the Constant Scan operator. But enabling the constraint without validating the existing data will again result in a Table Scan even when no records are returned. Basically, in this last case the query optimizer can not trust the check constraint.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;You can test yourself the resulting execution plans of the query after enabling the check constraint with any of these two commands&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:green;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;-- enable constraint validating existing data &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;table&lt;/SPAN&gt; albums &lt;SPAN style="COLOR:blue;"&gt;with&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;check&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;check&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;constraint&lt;/SPAN&gt; check_year &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:green;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;-- enable constraint without validating existing data &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;alter&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:blue;"&gt;table&lt;/SPAN&gt; albums &lt;SPAN style="COLOR:blue;"&gt;with&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;nocheck&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;check&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;constraint&lt;/SPAN&gt; check_year &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;By the way, you can use the following query to see if a check constraint is not trusted, that is, if it has not been verified for all the records on the table.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; name&lt;SPAN style="COLOR:gray;"&gt;,&lt;/SPAN&gt; is_not_trusted &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; &lt;SPAN style="COLOR:green;"&gt;sys&lt;/SPAN&gt;&lt;SPAN style="COLOR:gray;"&gt;.&lt;/SPAN&gt;&lt;SPAN style="COLOR:green;"&gt;check_constraints &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;&lt;B style="mso-bidi-font-weight:normal;"&gt;Contradicting Predicate &lt;/B&gt;&lt;/P&gt;
&lt;P&gt;The second type of contradiction case is when the query itself contains a contradiction. Take a look at this query&lt;/P&gt;
&lt;P class=MsoNormal&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;select&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; &lt;SPAN style="COLOR:gray;"&gt;*&lt;/SPAN&gt; &lt;SPAN style="COLOR:blue;"&gt;from&lt;/SPAN&gt; albums &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;where&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; album_year &lt;SPAN style="COLOR:gray;"&gt;&amp;lt;&lt;/SPAN&gt; 1970 &lt;SPAN style="COLOR:gray;"&gt;and&lt;/SPAN&gt; album_year &lt;SPAN style="COLOR:gray;"&gt;&amp;gt;&lt;/SPAN&gt; 1980 &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;option &lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:gray;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;(&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;COLOR:blue;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt;maxdop&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE:10pt;FONT-FAMILY:'Courier New';mso-no-proof:yes;"&gt; 1&lt;SPAN style="COLOR:gray;"&gt;) &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;/SPAN&gt;Same as before the execution plan for this query will use a Table Scan when the query hint is not used even when it is so obvious that there is a contradiction and no records should be returned. &lt;/P&gt;
&lt;P class=MsoNormal&gt;Once I add the query hint,&amp;nbsp;the Table Scan again is gone and only a Constant Scan operator is used.&lt;/P&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=15733" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Check+Constraints/default.aspx">Check Constraints</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Contradiction+Detection/default.aspx">Contradiction Detection</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Optimization/default.aspx">Optimization</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/ben_nevarez/archive/tags/Query+Optimizer/default.aspx">Query Optimizer</category></item></channel></rss>