<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.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>Ian Jose's WebLog</title><link>http://blogs.msdn.com/ianjo/default.aspx</link><description>This weblog is intended to help T-SQL developers get the best performing query plans from SQL Server.  Some of the posts describe simple mistakes that can be easily avoided.  Other posts describe complex solutions to limitations in SQL Server.  Lastly, some posts describe recent improvements in SQL Server, and the means to employ them best.</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Ascending Keys and Auto Quick Corrected Statistics</title><link>http://blogs.msdn.com/ianjo/archive/2006/04/24/582227.aspx</link><pubDate>Mon, 24 Apr 2006 18:50:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:582227</guid><dc:creator>ianjo</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/582227.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=582227</wfw:commentRss><description>&lt;P&gt;A common problem for some SQL Server applications are cases where data typically ascends.&amp;nbsp; For example, datetime columns where the column represents a current date.&amp;nbsp; SQL Server builds statistics with the assumption that the data will by in large be similar in the future.&amp;nbsp; However, when data typically ascends, most new&amp;nbsp;insertions are out of the previously found range.&amp;nbsp; This can lead to poorly performing plans as filters selecting recent data seem to exclude the entire relation when in fact a significant number of rows are included.&lt;/P&gt;
&lt;P&gt;Trace flag 2389 and 2390, both new in SQL Server 2005 SP1, can help to address this problem.&amp;nbsp; SQL Server 2005 SP1 begins to track the nature of columns via subsequent operations of updating statistics.&amp;nbsp; When the statistics are seen to increase three times the column is branded ascending.&amp;nbsp; If trace flag 2389 is set, and a column is branded ascending, and a covering index exists with the ascending column as the leading key, then the statistics will be updated automatically at query compile time.&amp;nbsp; A statement is compiled to find the highest value and a new step is added at the end of the existing histogram to model the recently added data.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Trace flag 2390 enables the same behavior even if the ascending nature of the column is not known.&amp;nbsp; As long as the column is a leading column in an index, then the optimizer will refresh the statisitc (with respect to the highest value) at query compile time.&amp;nbsp; Never use 2390 alone since this would mean that this logic would be disabled as soon as the ascending nature of the column was known.&lt;/P&gt;
&lt;P&gt;-- enable auto-quick-statistics update for known ascending keys&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;dbcc traceon( 2389 )&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;-- neable auto-quick-statistics update for all columns, known ascending or unknown&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;dbcc traceon( 2389, 2390 )&lt;/STRONG&gt; -- never enable 2390 alone&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=582227" width="1" height="1"&gt;</description></item><item><title>Query Processor Modelling Extensions in SQL Server 2005 SP1</title><link>http://blogs.msdn.com/ianjo/archive/2006/04/24/582219.aspx</link><pubDate>Mon, 24 Apr 2006 18:28:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:582219</guid><dc:creator>ianjo</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/582219.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=582219</wfw:commentRss><description>&lt;P&gt;Trace flag 2301, available in SQL Server 2005 SP1, enhances the modelling ability of the query optimizer to better handle complex statements.&amp;nbsp; Improved modelling can lead to dramatically faster performing query plans in some cases.&amp;nbsp; These extensions to the query processor&amp;nbsp;modelling abilities&amp;nbsp;can lead to increased compile time and so should only be used by applications which compile infrequently.&amp;nbsp; The model extensions are as follows:&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;&lt;STRONG&gt;Integer Modelling&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Normally, histogram modelling assumes that values between histogram steps are equally distributed to every numerical double code point.&amp;nbsp; This modelling extension remembers, for integer base types, that values can only occur on integer code points and this improves cardinality estimates for inequality filters.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Comprehensive Histogram Usage&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Normally, histograms are ignored when the cardinality of a relation dropps below the number of steps in a histogram.&amp;nbsp; This is a heuristic which captures the liklihood that a histogram continues to describe a relation.&amp;nbsp; This modelling extension&amp;nbsp;applies the histogram in cardinality estimate regardless of the cardinality estimate for the relation.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Base Containment Assumption&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Normally, when two relations are joined, we assume that X distinct code points in the same key range on input relation R will join with Y distinct code points in the same key range on input relation S such that MIN(X,Y) will find matches.&amp;nbsp; This assumption is called Simple Containment.&amp;nbsp; We assume that the smaller number of distinct code points match with code points from the other side.&amp;nbsp; This modelling ignores the relative population of distinct code points in the base forms of R and S, and also ignores any filtering that has occured to the base forms for R and S before joining.&amp;nbsp; Base containment applies the containment assumption only to the base relations and uses probabilistic methods to compute the degree of joining.&amp;nbsp; In addition, implied filters are modelled correctly since their behavior is very different from orthogonal filters.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Comprehensive Density Remapping&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Normally, when columns are CONVERTed only a small number of densities involving such columns are remapped to the new column definitions.&amp;nbsp; Note that operations like convert rarely change the density of a column.&amp;nbsp; Density is the measure of the number&amp;nbsp;of duplicate values for each distinct value.&amp;nbsp; With this modelling extension, all such remappings are applied which makes possible subsequent density matching for the purposes of cardinality estimation.&amp;nbsp; In some cases, this can lead to excessive use of memory.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Comprehensive Density Matching&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Normally, densities are matched when the very same base column is filtered or joined.&amp;nbsp; With this modelling extension, the notion of equivalence of columns as a result of equi-joins is applied leading to more complete density matching for the purposes of improved cardinality estimation.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;These extentions all were developed to address customer found problems relation to poor performing query plans.&amp;nbsp; If customers experience such poor performing plans where one or more of the above extentions may help, then trace flag 2301 may be applied.&amp;nbsp; It is important to note that compile times will increase, and in some cases memory consumption can increase dramatically.&amp;nbsp; Thus, it is important to apply this trace flag with care and test exhaustively before using in production.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=582219" width="1" height="1"&gt;</description></item><item><title>Disabling Constant-Constant Comparison Estimation</title><link>http://blogs.msdn.com/ianjo/archive/2006/03/28/563419.aspx</link><pubDate>Tue, 28 Mar 2006 23:43:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:563419</guid><dc:creator>ianjo</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/563419.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=563419</wfw:commentRss><description>&lt;P&gt;SQL Server 8.0 did not perform cardinality estimates based on the comparion of two constants.&amp;nbsp; Instead, SQL Server 8.0 guessed at the resulting selectivity.&amp;nbsp; The reasoning for this is that one or more of the constants may be statement parameters, which would change from one execution of the statement to the next.&amp;nbsp; However, SQL Server 9.0 reversed this behavior and does esimate operator cardinality based on the comparison of two constants, when both values are available at compile time.&amp;nbsp; Constant values are avilable at compile when when 1) they are literal constants or 2) they are parameters to a stored procedure or otherwise set at the nesting level above the the compilation of the statement.&amp;nbsp; This change in behavior can be problematic for statements that optimize for one set of values but run for other sets of values.&amp;nbsp; Applications that find adverse plan changes resulting from the change in behvior can revert behavior&amp;nbsp;to that of the previous released by enabling trace flag &lt;STRONG&gt;2328&lt;/STRONG&gt;.&amp;nbsp; For example, to revert the behavior server wide, for the current invokation of SQL Server, one could run the following command:&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;dbcc traceon( 2328, -1)&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;If running SQL Server 8.0, or SQL Server 9.0 with trace flag 2328 enabled, the optimizer will guess the selectivity arising from the comparion of two constants.&amp;nbsp; The guess used is the same as comparing a column to an constant (where&amp;nbsp;either the column&amp;nbsp;distribution or the constant&amp;nbsp;value, or both, is unknown).&amp;nbsp; Since the selectivity of the comparison of two constants is either 0 or 1, one might think that a guess for such a comparion wouldbe 50%.&amp;nbsp; Although there is a lot of logic in this behavior, for historic reasons, the guess used by SQL Server was very different.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Guessing the selectivity of a column-constant comparsion attempts to model several phenomena.&amp;nbsp; First, when there are many rows, the selectivity is likely smaller because there could be more values from which to choose.&amp;nbsp; Second, when there are many such conjuncts, it is more likely that subsequent conjuncts will be correlated with previous conjucts.&amp;nbsp; SQL Server therefore reduces the selectivity from one to 0 by the exponent of the cardinality and the reducition factor is reduced with each guess.&amp;nbsp; The following table shows the number of conjucts guessed and the resultant selectivity as a function of input table cardinality of N:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;# Conjucts&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Cardinality&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Selectivity&lt;/P&gt;
&lt;P&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(3/4)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(-1/4)&lt;/P&gt;
&lt;P&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(11/16)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(-5/16)&lt;/P&gt;
&lt;P&gt;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(43/64)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(-21/64)&lt;/P&gt;
&lt;P&gt;4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(171/256)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(-85/256)&lt;/P&gt;
&lt;P&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(170/256)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(-86/256)&lt;/P&gt;
&lt;P&gt;6&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(169/256)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(-87/256)&lt;/P&gt;
&lt;P&gt;7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(168/256)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(-88/256)&lt;/P&gt;
&lt;P&gt;...&lt;/P&gt;
&lt;P&gt;175&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(0/256)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;N^(-1)&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=563419" width="1" height="1"&gt;</description></item><item><title>Make Functions Schema-bound</title><link>http://blogs.msdn.com/ianjo/archive/2006/01/31/521078.aspx</link><pubDate>Wed, 01 Feb 2006 00:42:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:521078</guid><dc:creator>ianjo</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/521078.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=521078</wfw:commentRss><description>&lt;p&gt;Create user defined functions with the &lt;strong&gt;SCHEMABINDING&lt;/strong&gt; clause where possible.&amp;nbsp; In the absence of this clause, SQL Server must assume at compile time that statements using the function may bind to a function which accesses and updates data.&amp;nbsp; Note that even if the UDFs do not access data or perform any updates, the SQL Server optimizer must be prepared for this in case the statement at run-time binds to a different UDF which does access and update data.&amp;nbsp; This leads the optimizer to add halloween protection that is often unneccessary.&amp;nbsp; This protection often manifests as Spool or Sort iterators in the plans and can dramatically slow down plan performance.&amp;nbsp; Making UDFs schema-bound is necessary even if the UDF is uniquely named, and even if no UDFs access or update data.&amp;nbsp; &lt;/p&gt;
&lt;p&gt;This behavior has changed in SQL Server 2005, and applications that are porting from SQL Server 2000 to SQL Server 2005 should re-examine whether their UDFs can be schema bound and to make them schema bound if they are not already so marked.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;Background on Halloween Protection.&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;"Halloween protection" in database systems refers to a solution for a problem that can occur in update queries. The problem&amp;nbsp;occurs when an&amp;nbsp;update itself affects the rows selected for update.&amp;nbsp; For example, imagine a company wants to give every employee a 10% pay increase. If the update query is unlucky enough to walk the salary index then the same row may be selected, updated, the update moves the row ahead in the index, and then the row will be redundantly selected again and again.&amp;nbsp; This problem is corrected by isolating the rows chosen from the effects of the update itself.&amp;nbsp; For example, a&amp;nbsp;SPOOL operation which stores all the rows to be updated outside of the context and any index can provide the necessary isolation. SORTs are also sufficient for isolation purposes.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=521078" width="1" height="1"&gt;</description></item><item><title>Regularly Update Statistics for Ascending Keys</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491551.aspx</link><pubDate>Fri, 11 Nov 2005 01:02:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491551</guid><dc:creator>ianjo</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491551.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491551</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;Ascending key columns, such as IDENTITY columns or datetime columns representing real-world timestamps, can cause inaccurate statistics in tables with frequent INSERTS because new values all lie outside the histogram.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Consider updating statistics on such columns frequently with a batch job if your application seems to be getting inadequate query plans for queries that have a condition on the ascending key column. How often to run the batch job depends on your application. Consider daily or weekly intervals, or more often if needed for your application. Alternatively, trigger the job based on an application event, such as after a bulk load or after a certain number of INSERT operations.&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491551" width="1" height="1"&gt;</description></item><item><title>Create Statistics for All Union Inputs</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491550.aspx</link><pubDate>Fri, 11 Nov 2005 01:01:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491550</guid><dc:creator>ianjo</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491550.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491550</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;When a query requires statistics on the result of a &lt;?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /&gt;&lt;st1:place&gt;UNION&lt;/st1:place&gt; or UNION ALL operation, create needed statistics uniformly on all &lt;st1:place&gt;UNION&lt;/st1:place&gt; or UNION ALL inputs.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;It is not sufficient to create statistics on a subset of the &lt;st1:place&gt;UNION&lt;/st1:place&gt; or UNION ALL inputs, even when those inputs are dominant.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;For example, &lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; TEXT-INDENT: 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;SELECT * FROM Lineitem l WHERE EXISTS &lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; TEXT-INDENT: 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;( SELECT * FROM Region1 r1 WHERE r1.C = l.C and r1.S = l.S &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; TEXT-INDENT: 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;UNION SELECT * FROM Region2 r2 WHERE r2.C = l.C and r2.S = l.S ) &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;In order for the EXISTS to be optimized accurately, it may be necessary to have a multi-column statistic on columns C and S in for Lineitem, Region1 and Region2.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;If the multi-column statistic does not exist in either Region1 or Region2 then the remaining statistics cannot be used to optimize this query.&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491550" width="1" height="1"&gt;</description></item><item><title>Auto-create and Auto-update Statistics</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491549.aspx</link><pubDate>Fri, 11 Nov 2005 01:01:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491549</guid><dc:creator>ianjo</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491549.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491549</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;For a large majority of SQL Server installations, the most important best practice is to use auto create and auto update statistics database-wide. Auto create and auto update statistics are on by default. If you observe bad plans and suspect that missing or out of date statistics are at fault, verify that auto create and auto update statistics are on. &lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;An alternative to enabling auto create statistics is enabled or make sure to manually create statistics using CREATE STATISTICS or &lt;B style="mso-bidi-font-weight: normal"&gt;sp_createstats&lt;/B&gt;.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Note that auto-statistics will not work for read-only databases.&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491549" width="1" height="1"&gt;</description></item><item><title>Create Multi-Column Statistics</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491548.aspx</link><pubDate>Fri, 11 Nov 2005 01:01:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491548</guid><dc:creator>ianjo</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491548.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491548</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;When a query has a multi-column condition, consider creating multi-column statistics if you suspect that the optimizer is not producing the best plan for the query. You get multi-column statistics as a by-product of creating a multi-column index, so if there is already a multi-column index that supports the multi-column condition, there is no need to create statistics explicitly. Auto create statistics only creates single-column statistics, never multi-column statistics. So if you need multi-column statistics, create them manually, or create a multi-column index.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;Consider a query that accesses the AdventureWorks.Person.Contact table, and contains the following condition:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;FirstName = 'Catherine' AND LastName = 'Abel'&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;To make selectivity estimation more accurate for this query, create the following statistics object:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="COLOR: windowtext"&gt;CREATE STATISTICS &lt;/SPAN&gt;&lt;?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /&gt;&lt;st1:place&gt;&lt;st1:City&gt;&lt;SPAN style="COLOR: windowtext"&gt;LastFirst&lt;/SPAN&gt;&lt;/st1:City&gt;&lt;SPAN style="COLOR: windowtext"&gt; &lt;/SPAN&gt;&lt;st1:State&gt;&lt;SPAN style="COLOR: windowtext"&gt;ON&lt;/SPAN&gt;&lt;/st1:State&gt;&lt;/st1:place&gt;&lt;SPAN style="COLOR: windowtext"&gt; Person.Contact(LastName,FirstName)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;This statistics object will be useful for queries that contain predicates on LastName and FirstName, as well as LastName alone. In general, the selectivity of a predicate on any prefix of the set of columns in a multi-column statistics object can be estimated using that statistics object. &lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;For a statistics object to fully support a multi-column condition, a &lt;I style="mso-bidi-font-style: normal"&gt;prefix&lt;/I&gt; of the columns in the multi-column statistics object must contain the columns in the condition. For example, a multi-column statistics object on columns (a,b,c) only partially supports the condition a=1 AND c=1; the histogram will be used to estimate the selectivity for a=1, but the density information for c will not be used since b is missing from the condition. Multi-column statistics on (a,c) or (a,c,b) would support the condition a=1 AND c=1, and density information could be used to improve selectivity estimation.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491548" width="1" height="1"&gt;</description></item><item><title>Use Condition-Specific Stored Procedures</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491547.aspx</link><pubDate>Fri, 11 Nov 2005 01:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491547</guid><dc:creator>ianjo</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491547.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491547</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;The optimizer chooses the best plan for an SP given the current parameter values.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;This plan is then reused regardless of whether the nature of the parameters changes from call to call.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;If an application is aware that input parameters have a small number of significant behaviors, then the application may use condition-specific SPs.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;For example, if an SP has a single parameter which can be either NULL or non-NULL, and a NULL value selects very few rows while a non-NULL value selects many more, then this technique may be used.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The application would code the SP twice.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The two SPs will have different names but the SP contents will be the same.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The logic that calls the SP must call the appropriate SP depending on whether the parameter value is NULL or non-NULL.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;SPs are compiled the first time they are called.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;As a result, the SP for NULL will be optimized for a NULL value and will be subsequently only called when the parameter value is NULL.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The SP for a non-NULL parameter will similarly be optimized for a non-NULL parameter value and only be called with a non-NULL parameter value.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;This method can result in dramatic improvements in performance when the degree of selectivity of parameters varies significantly.&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491547" width="1" height="1"&gt;</description></item><item><title>Simplify statements with IF</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491546.aspx</link><pubDate>Fri, 11 Nov 2005 01:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491546</guid><dc:creator>ianjo</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491546.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491546</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;In some cases, a SQL statement can be simplified by using procedural logic.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Instead of issuing one complex query to cover multiple cases with &lt;?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /&gt;&lt;st1:place&gt;UNION&lt;/st1:place&gt; or OR, it is better to use and if..else logic to separate the cases into different SQL statements.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;In this way, the optimizer optimizes multiple simpler statements instead of a single complex statement and may be able to get better performing plans for each case.&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491546" width="1" height="1"&gt;</description></item><item><title>Limit Use of Multi-Statement TVFs and Table Variables</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491545.aspx</link><pubDate>Fri, 11 Nov 2005 01:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491545</guid><dc:creator>ianjo</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491545.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491545</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;Limit use of multi-statement table valued functions (TVFs) and table variables in situations where getting a high-performance plan is requied.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Both multi-statement TVFs and table variables have no statistics. The optimizer must guess the size of their results. Similarly, result column do not have statistics, and the optimizer must resort to guesses for predicates involving these columns.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;If a bad plan results because of these guesses, consider using an in-lined TVF, or standard table or temporary table as a temporary holding place for the results of the TVF, or a replacement for the table variable. This will allow the optimizer to use better cardinality estimates.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Note that this technique may also lead to an increased number of statement recompilations.&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491545" width="1" height="1"&gt;</description></item><item><title>Use Function Results</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491543.aspx</link><pubDate>Fri, 11 Nov 2005 00:59:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491543</guid><dc:creator>ianjo</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491543.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491543</wfw:commentRss><description>&lt;DIV class=Section1&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;Built-in functions with literal constant inputs are simplified during optimization to resultant constant values.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;However, user defined functions or built-in functions with variable&lt;/FONT&gt;&lt;A title="" style="mso-footnote-id: ftn1" href="#_ftn1" name=_ftnref1&gt;&lt;SPAN class=MsoFootnoteReference&gt;&lt;SPAN style="mso-special-character: footnote"&gt;&lt;SPAN class=MsoFootnoteReference&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 'Times New Roman'; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA"&gt;&lt;FONT color=#0000ff&gt;[1]&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;FONT face=Verdana size=2&gt; inputs are only simplified to resultant constants for the purposes of plan optimization for the following functions:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;FONT face=Verdana size=2&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;SPAN style="FONT-SIZE: 10pt; COLOR: black; FONT-FAMILY: Verdana; mso-bidi-font-family: 'Times New Roman'; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA"&gt;&lt;BR style="PAGE-BREAK-BEFORE: auto; mso-break-type: section-break" clear=all&gt;&lt;/SPAN&gt;
&lt;DIV class=Section2&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;Lower&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;Upper&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;RTrim&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;Datediff&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;Dateadd&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;Datepart&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;Substring&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;Charindex&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;Length&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;SUser_SName&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;IsNull&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;GetDate&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;FONT face=Verdana&gt;GetUTCDate&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: black; FONT-FAMILY: Verdana; mso-bidi-font-family: 'Times New Roman'; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA"&gt;&lt;BR style="PAGE-BREAK-BEFORE: always; mso-break-type: section-break" clear=all&gt;&lt;/SPAN&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;o:p&gt;&lt;FONT face=Verdana size=2&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;With other functions, the optimizer will guess the selectivity of predicates and this may lead to a poorly performing plan. &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;It is better for the query writer to set the value of the scalar expression to a variable and pass this value to the SQL statement as a parameter to a stored procedure or parameter to sp_executesql.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The query optimizer can determine the value contained within a parameter and will use this value to determine the best plan.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;In this way, the query optimizer will be able to optimize with the resultant expression value.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;If the expression is present in the SQL query, the optimizer will not know the resultant value and will guess the selectivity of predicates containing this expression.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;This can lead to poorly performing plans.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Note that column inputs to expressions must be present in the query statement since they require query context to have meaning.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;DIV style="mso-element: footnote-list"&gt;&lt;BR clear=all&gt;&lt;FONT face=Verdana size=2&gt;
&lt;HR align=left width="33%" SIZE=1&gt;
&lt;/FONT&gt;
&lt;DIV id=ftn1 style="mso-element: footnote"&gt;
&lt;P class=MsoFootnoteText style="MARGIN: 3pt 0in"&gt;&lt;A title="" style="mso-footnote-id: ftn1" href="#_ftnref1" name=_ftn1&gt;&lt;SPAN class=MsoFootnoteReference&gt;&lt;SPAN style="mso-special-character: footnote"&gt;&lt;SPAN class=MsoFootnoteReference&gt;&lt;SPAN style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana; mso-bidi-font-family: 'Times New Roman'; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA"&gt;&lt;FONT color=#0000ff&gt;[1]&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/A&gt;&lt;FONT face=Verdana color=#0000ff size=2&gt; Corelated parameter, local variable, or procedure parameter.&lt;/FONT&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;/DIV&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491543" width="1" height="1"&gt;</description></item><item><title>Avoid Unnecessary Data Type Conversions</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491541.aspx</link><pubDate>Fri, 11 Nov 2005 00:58:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491541</guid><dc:creator>ianjo</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491541.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491541</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;SQL Server adds implicit data type conversions when types don’t match.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;This can have unintended results both on query results, but also on the query plan.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;It is important to use literal constants that match in type column they are being compared with wherever possible.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;A common mistake in writing T-SQL is to always use string literals for constants even when those constants are numbers, for example.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;SELECT * FROM Sales&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;WHERE Quantity&amp;nbsp;= ‘100’ &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;In this example, ‘100’ is a character string while the column Quantity is an integer.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The implicit conversion used follows strict precedence rules.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Here the conversion will be on&amp;nbsp;Quantity to character string, and not on the ‘100’ to an integer.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491541" width="1" height="1"&gt;</description></item><item><title>Limit Non-order Preserving Expressions</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491540.aspx</link><pubDate>Fri, 11 Nov 2005 00:58:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491540</guid><dc:creator>ianjo</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491540.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491540</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;Expressions with column transformations that do not retain the original column order do not benefit from column statistics.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;This can lead to poor plans.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Assume for purposes of illustration that we've added a column to Sales.SalesOrderHeader in AdventureWorks as follows:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; TEXT-INDENT: 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;ALTER TABLE Sales.SalesOrderHeader ADD SalesOrderFlag tinyint&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;For example, consider the following:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;SELECT * FROM Sales.SalesOrderHeader h&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;WHERE h.SalesOrderFlag &amp;amp; 0x0001 = 1&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;If a predicate on a column performs a bit-wise AND on the column, the selectivity of the predicate cannot in general be estimated using the column histogram.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Here, it would be better to separate out the bit flag as its own column on which statistics could be built.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;For example:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;ALTER TABLE Sales.SalesOrderHeader ADD SalesOrderValidFlag tinyint&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;In the latter case the selectivity of a predicate on the flag which is no longer bit-wise ANDed could be estimated, as shown below.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; TEXT-INDENT: 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;SELECT * FROM Sales.SalesOrderHeader h&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;WHERE h.SalesOrderValidFlag = 1&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;Alternatively, a computed column could be defined on the bit-wise ANDed value, and a statistic could be built on the computed column.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The computed column would then be referenced in the query.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;There are many cases of non-order preserving expressions.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Consider the query shown below.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;SELECT * FROM Sales&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;WHERE Price * ( 1 + Tax ) &amp;gt; 100&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;If you see slower-than-desired query performance in such a case, consider creating a computed column with the equivalent expression, and creating statistics or an index on the computed column. Auto create statistics will also create statistics for the computed column if it exists, so you need not create the computed column statistics manually if auto create statistics is enabled. You do not have to modify the query to reference the computed column explicitly because query expressions are matched to identical computed column expressions automatically.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491540" width="1" height="1"&gt;</description></item><item><title>Use Parameters or Literals for Query Inputs</title><link>http://blogs.msdn.com/ianjo/archive/2005/11/10/491538.aspx</link><pubDate>Fri, 11 Nov 2005 00:54:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:491538</guid><dc:creator>ianjo</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/ianjo/comments/491538.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ianjo/commentrss.aspx?PostID=491538</wfw:commentRss><description>&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;Use unmodified parameters or literal constants in query statements to ensure that the optimizer can determine a representative value and optimize accordingly.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;If you use a local variable in a query predicate instead of a parameter or literal, the optimizer is unable to determine the value for the input.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Likewise, if a parameter value is modified before being used in a statement, then the query plan will be chosen for the original value of the parameter and may not be the best plan for the actual value of the parameter when the statement is executed.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Not having the proper value, or any value at all, leads to a less accurate cardinality estimates. &lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;Poor estimates can in turn lead to a poorly performing query plan.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Use unmodified parameters or literals in the query instead of local variables, and the optimizer typically will be able to pick a better query plan. For example, consider this query that uses a local variable:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; mso-pagination: widow-orphan lines-together"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;declare @StartOrderDate datetime&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; mso-pagination: widow-orphan lines-together"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;set @StartOrderDate = '20040731'&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; mso-pagination: widow-orphan lines-together"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;select * from Sales.SalesOrderHeader h, Sales.SalesOrderDetail d&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; mso-pagination: widow-orphan lines-together"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;WHERE h.SalesOrderID = d.SalesOrderId&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 3pt; mso-pagination: widow-orphan lines-together"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;AND h.OrderDate &amp;gt;= @StartOrderDate&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;The number of rows from Sales.SalesOrderHeader that the optimizer estimates will qualify vs. the condition &lt;/FONT&gt;&lt;SPAN style="COLOR: windowtext; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;h.OrderDate &amp;gt;= @StartOrderDate &lt;/SPAN&gt;&lt;FONT face=Verdana&gt;&lt;SPAN style="COLOR: windowtext; mso-bidi-font-family: 'Courier New'; mso-no-proof: yes"&gt;is 9439.5, which is exactly 30% of the size of the table. You can use the graphical showplan for the query and right-click the plan node for Sales.SalesOrderHeader to display this cardinality estimate. In a pre-release version of SQL Server&lt;/SPAN&gt;&amp;nbsp;&lt;SPAN style="COLOR: windowtext; mso-bidi-font-family: 'Courier New'; mso-no-proof: yes"&gt;2005 used while preparing this paper, the plan chosen uses a merge join (the observations that follow are based on this same SQL Server&lt;/SPAN&gt;&amp;nbsp;&lt;SPAN style="COLOR: windowtext; mso-bidi-font-family: 'Courier New'; mso-no-proof: yes"&gt;2005 version; your results may differ depending on your SQL Server version, available memory, etc.). Now, consider this equivalent query that doesn't use a local variable:&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;SELECT * FROM Sales.SalesOrderHeader h, Sales.SalesOrderDetail d&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;WHERE h.SalesOrderID = d.SalesOrderId&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="COLOR: windowtext"&gt;AND h.OrderDate &amp;gt;= '20040731'&lt;/SPAN&gt;&lt;SPAN style="COLOR: windowtext; FONT-FAMILY: Verdana"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;The cardinality of the result set for the predicate &lt;/FONT&gt;&lt;SPAN style="FONT-FAMILY: 'Courier New'"&gt;"h.OrderDate &amp;gt;= '20040731'"&lt;/SPAN&gt;&lt;FONT face=Verdana&gt; is estimated as 40 in the graphical showplan for the query (right-click the &lt;B style="mso-bidi-font-weight: normal"&gt;filter&lt;/B&gt; operator), for a selectivity of 0.13%. The plan chosen for this query uses a nested loop join instead of a merge join because of this improved estimate.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;Even when local variables are used in a query, an estimate that is better than a guess is used in the case of equality predicates. Selectivity for conditions of the form &lt;/FONT&gt;&lt;SPAN style="FONT-FAMILY: 'Courier New'"&gt;"@local_variable = column_name"&lt;/SPAN&gt;&lt;FONT face=Verdana&gt; is estimated using the average value frequency from the histogram for &lt;/FONT&gt;&lt;SPAN style="FONT-FAMILY: 'Courier New'"&gt;column_name&lt;/SPAN&gt;&lt;FONT face=Verdana&gt;. So, for example, if the column &lt;/FONT&gt;&lt;SPAN style="FONT-FAMILY: 'Courier New'"&gt;column_name&lt;/SPAN&gt;&lt;FONT face=Verdana&gt; contains all unique values, then a selectivity estimate of 1/(number of unique values in column) will be used, which is accurate.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;To eliminate the use of local variables, consider (1) rewriting the query to use literals instead of variables, (2) using &lt;B style="mso-bidi-font-weight: normal"&gt;sp_executesql&lt;/B&gt; with parameters that replace your use of local variables, or (3) using a stored procedure with parameters that replace your use of local variables. Dynamic SQL via EXEC may also be useful for eliminating local variables, but it typically will result in higher compilation overhead.&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;The query optimizer chooses the best plan for the value of the parameter that was input to the stored procedure or call to &lt;B style="mso-bidi-font-weight: normal"&gt;sp_executesql&lt;/B&gt;.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;For this reason, it is best to avoid writing SQL where the value used at execution is different from the value input to the stored procedure.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Instead it is better to modify the parameter value in one stored procedure and then pass it to another stored procedure which contains the SQL statement where it is used.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;For example, consider this query that modifies a parameter value:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;CREATE PROCEDURE GetRecentSales (@date datetime) WITH RECOMPILE AS&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;BEGIN&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;IF @date IS NULL&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;SET @date = dateadd("mm",-3,(SELECT MAX(OrderDATE) &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 7"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;FROM Sales.SalesOrderHeader))&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;SELECT * FROM Sales.SalesOrderHeader h, Sales.SalesOrderDetail d&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;WHERE h.SalesOrderID = d.SalesOrderID&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;AND h.OrderDate &amp;gt; @date&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;END&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT size=2&gt;&lt;FONT face=Verdana&gt;&lt;SPAN style="mso-no-proof: yes"&gt;This SP, if called with NULL, will have the final SELECT statement optimized for @date&lt;/SPAN&gt;&amp;nbsp;&lt;SPAN style="mso-no-proof: yes"&gt;=&lt;/SPAN&gt;&amp;nbsp;&lt;SPAN style="mso-no-proof: yes"&gt;NULL. Since no rows have NULL OrderDate, the cardinality estimate for the result of applying this filter to SalesOrderHeader is very low (1&lt;/SPAN&gt;&amp;nbsp;&lt;SPAN style="mso-no-proof: yes"&gt;row). However, at run time, the date is not NULL, but three months before the latest OrderDate.&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;The actual number of SalesOrderHeader rows that qualify is 5,736. The optimizer chooses a nested loop join for the query when NULL is passed to GetRecentSales, whereas the optimal plan contains a merge join. You can see the plan selected, and the expected and actual cardinalities, using this script:&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;SET STATISTICS PROFILE ON&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;GO&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;EXEC GetRecentSales NULL&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;GO&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;SET STATISTICS PROFILE OFF&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT size=2&gt;GO&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Text style="MARGIN: 3pt 0in"&gt;&lt;FONT face=Verdana size=2&gt;The WITH RECOMPILE option specified on the GetRecentSales stored procedure above does not eliminate the cardinality estimation error. One way to ensure that the queries in this example are optimized with appropriate parameter values that allow good estimates to be obtained is to modify the stored procedure as follows, breaking it down into parts:&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;CREATE PROCEDURE GetRecentSales (@date datetime)&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;AS&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;BEGIN&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;IF @date IS NULL&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;SET @date = dateadd("mm",-3,(SELECT MAX(OrderDATE) &lt;SPAN style="mso-tab-count: 2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;FROM Sales.SalesOrderHeader))&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;EXEC GetRecentSalesHelper @date&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;END&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;CREATE PROCEDURE GetRecentSalesHelper (@date datetime) WITH RECOMPILE AS&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;BEGIN&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;SELECT * FROM Sales.SalesOrderHeader h, Sales.SalesOrderDetail d&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;WHERE h.SalesOrderID = d.SalesOrderID&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;AND h.OrderDate &amp;gt; @date -- @date is unchanged from compile time,&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;&lt;SPAN style="mso-tab-count: 5"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;-- so a good plan is obtained.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=Code style="MARGIN: 0in 0in 0pt"&gt;&lt;SPAN style="COLOR: windowtext"&gt;&lt;FONT size=2&gt;&lt;FONT face="Courier New"&gt;END&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=491538" width="1" height="1"&gt;</description></item></channel></rss>