<?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>Paul White: Page Free Space</title><link>http://sqlblog.com/blogs/paul_white/default.aspx</link><description>Technical stuff about SQL Server - from the Kāpiti Coast, New Zealand</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>Dynamic Seeks and Hidden Implicit Conversions</title><link>http://sqlblog.com/blogs/paul_white/archive/2012/01/18/dynamic-seeks-and-hidden-implicit-conversions.aspx</link><pubDate>Tue, 17 Jan 2012 13:37:32 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:41097</guid><dc:creator>Paul White</dc:creator><slash:comments>13</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/41097.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=41097</wfw:commentRss><description>&lt;p&gt;Most people know that a LIKE predicate with only a trailing wildcard can usually use an index seek:&lt;/p&gt;  &lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;WHERE&lt;/span&gt; &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name &lt;span style="color:#0000ff;"&gt;LIKE&lt;/span&gt; N&lt;span style="color:#006080;"&gt;'D%'&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_3C27CA3C.png" target="_blank"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="Index Seek on LIKE" border="0" alt="Index Seek on LIKE" src="http://sqlblog.com/blogs/paul_white/image_thumb_4B7B2C4B.png" width="403" height="334" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the execution plan shows, SQL Server determines a covering range (which depends on the collation), seeks the string index using the range as the start and end points of a partial scan, and applies the original LIKE condition as a residual predicate to just the rows that match the initial seek operation.&amp;#160; Specifically, the Storage Engine seeks the index to locate rows in the covering range, and the Query Processor applies the residual predicate (the LIKE) to the rows it receives.&lt;/p&gt;

&lt;h3&gt;Dynamic Seeks&lt;/h3&gt;

&lt;p&gt;But what if the LIKE search term is in a variable?&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DECLARE&lt;/span&gt; @&lt;span style="color:#0000ff;"&gt;Like&lt;/span&gt; NVARCHAR(50) = N&lt;span style="color:#006080;"&gt;'D%'&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&amp;#160;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;WHERE&lt;/span&gt; &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name &lt;span style="color:#0000ff;"&gt;LIKE&lt;/span&gt; @&lt;span style="color:#0000ff;"&gt;Like&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;SQL Server can still perform a seek here, but it needs to determine the covering seek range for the search term at execution time, not at compilation time:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_4FA50410.png" target="_blank"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="Dynamic Index Seek" border="0" alt="Dynamic Index Seek" src="http://sqlblog.com/blogs/paul_white/image_thumb_5362A8E0.png" width="632" height="483" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The plan now contains an extra Constant Scan,&amp;#160; a Compute Scalar and a Nested Loops Join.&amp;#160; These operators are interesting because they have zero cost estimates: no CPU, no I/O, nothing.&amp;#160; That’s because they are purely architectural: a workaround for the fact that SQL Server cannot currently perform a dynamic seek within the Index Seek operator itself.&amp;#160; To avoid affecting plan choices, this extra machinery is costed at zero.&lt;/p&gt;

&lt;p&gt;The Constant Scan produces a single in-memory row with no columns.&amp;#160; The Compute Scalar defines expressions to describe the covering seek range (using the runtime value of the @Like variable).&amp;#160; Finally, the Nested Loops Join drives the seek using the computed range information as correlated values.&lt;/p&gt;

&lt;p&gt;The upper tooltip shows that the Compute Scalar uses three internal functions, &lt;strong&gt;LikeRangeStart&lt;/strong&gt;, &lt;strong&gt;LikeRangeEnd&lt;/strong&gt;, and &lt;strong&gt;LikeRangeInfo&lt;/strong&gt;.&amp;#160; The first two functions describe the range as an open interval.&amp;#160; The third function returns a set of flags encoded in an integer, that are used internally to define certain seek properties for the Storage Engine.&amp;#160; The lower tooltip shows the seek on the open interval described by the result of &lt;em&gt;LikeRangeStart&lt;/em&gt; and &lt;em&gt;LikeRangeEnd&lt;/em&gt;, and the application of the residual predicate ‘LIKE @Like’.&lt;/p&gt;

&lt;h3&gt;More Dynamic Seeks&lt;/h3&gt;

&lt;p&gt;Something very similar occurs in plans that use IN or OR with variables:&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DECLARE&lt;/span&gt; &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    @1 &lt;span style="color:#0000ff;"&gt;INTEGER&lt;/span&gt; = 320,&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    @2 &lt;span style="color:#0000ff;"&gt;INTEGER&lt;/span&gt; = 325,&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    @3 &lt;span style="color:#0000ff;"&gt;INTEGER&lt;/span&gt; = 330&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&amp;#160;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;WHERE&lt;/span&gt; &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.ProductID &lt;span style="color:#0000ff;"&gt;IN&lt;/span&gt; (@1,@2,@3);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_506D442D.png" target="_blank"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="Dynamic Seek With IN and OR" border="0" alt="Dynamic Seek With IN and OR" src="http://sqlblog.com/blogs/paul_white/image_thumb_17FF1D55.png" width="647" height="574" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have three ranges: one for each of the variables in the original query.&amp;#160; The Compute Scalar operators again define three columns containing the start and end of the range, and the associated informational flags (previously seen as a result of the &lt;em&gt;LikeRangeInfo&lt;/em&gt; function).&amp;#160; This time, we see the decimal representation of these flags, which happens to be 62 for an equality comparison.&amp;#160; The IN expands to (ProductID = @1 OR ProductID = @2 OR ProductID = @3), so each of the ‘ranges’ here is in fact a single value, so the start and end range values are the same in each Compute Scalar.&lt;/p&gt;

&lt;p&gt;The three dynamic ranges are concatenated, sorted (so any overlapping ranges appear next to each other in the stream) and the Merge Interval collapses these intervals into one or more disjoint (non-overlapping) ranges.&amp;#160; This is important, because the three variables might, for example, all contain the same value, and it would be incorrect to return that value three times.&amp;#160; Anyway, for each disjoint range produced, the Nested Loops Join drives a new seek of the Clustered Index.&amp;#160; The overall effect is that an arbitrary number of possibly overlapping ranges are computed, merged, and then used to drive one or more seek operations. The final result of the query will be the combination of all the seek results, as you would expect.&lt;/p&gt;

&lt;h3&gt;Hidden Conversions&lt;/h3&gt;

&lt;p&gt;The following example contains a table with DATETIME2 values, and a query with a expression that at first sight seems unlikely to be able to seek on an index (the variable is typed as DATE, and there is a CONVERT function applied to the DATETIME2 column):&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DECLARE&lt;/span&gt; @Example &lt;span style="color:#0000ff;"&gt;TABLE&lt;/span&gt; (date_time DATETIME2 &lt;span style="color:#0000ff;"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;KEY&lt;/span&gt;);&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&amp;#160;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;INSERT @Example (date_time) &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;VALUES&lt;/span&gt; (&lt;span style="color:#006080;"&gt;'20110101 12:34:56'&lt;/span&gt;);&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&amp;#160;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DECLARE&lt;/span&gt; @&lt;span style="color:#0000ff;"&gt;date&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;DATE&lt;/span&gt; = &lt;span style="color:#006080;"&gt;'2011-01-01'&lt;/span&gt;;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&amp;#160;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; * &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; @Example &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; e &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;WHERE&lt;/span&gt; &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    @&lt;span style="color:#0000ff;"&gt;date&lt;/span&gt; = &lt;span style="color:#0000ff;"&gt;CONVERT&lt;/span&gt;(&lt;span style="color:#0000ff;"&gt;DATE&lt;/span&gt;, e.date_time);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Nevertheless, a query plan that uses a seek can be produced:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_02C0F1E0.png" target="_blank"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="GetRangeThroughConvert" border="0" alt="GetRangeThroughConvert" src="http://sqlblog.com/blogs/paul_white/image_thumb_2DB8DFE5.png" width="702" height="461" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, neither SSMS or Plan Explorer will show the contents of the Compute Scalar (this is probably just an oversight, rather than deliberate concealment!).&amp;#160; We have to open the XML form of the execution plan to see the three familiar expressions, wrapped in a Value Vector (just a fancy container for multiple expressions).&lt;/p&gt;

&lt;p&gt;Another internal function, &lt;strong&gt;GetRangeThroughConvert&lt;/strong&gt;, is responsible for determining the the range of DATETIME2 values covered by the DATE variable @date, and the informational flags needed.&amp;#160; In the same way the engine works out covering ranges for some LIKE predicates, this function determines ranges where certain problematic type conversions are required.&amp;#160; Otherwise, the machinery is the same: a range description is defined by the Compute Scalar, and the Nested Loops Join driving a seek using those values.&lt;/p&gt;

&lt;h3&gt;More Hidden Conversions&lt;/h3&gt;

&lt;p&gt;There is another related internal function used when the Query Processor needs to determine a range for a comparison between different data types.&amp;#160; This example returns rows based on a greater-than-or-equal comparison between DATE column values and the DATETIME return value of the GETDATE() intrinsic function:&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DECLARE&lt;/span&gt; @Example &lt;span style="color:#0000ff;"&gt;TABLE&lt;/span&gt; (col1 &lt;span style="color:#0000ff;"&gt;date&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;KEY&lt;/span&gt;)&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; * &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; @Example &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; e &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;WHERE&lt;/span&gt; e.col1 &amp;gt;= DATEADD(&lt;span style="color:#0000ff;"&gt;DAY&lt;/span&gt;, -7, GETDATE());&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_1C850242.png" target="_blank"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="GetRangeWithMismatchedTypes" border="0" alt="GetRangeWithMismatchedTypes" src="http://sqlblog.com/blogs/paul_white/image_thumb_6EB7397C.png" width="541" height="351" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, the SSMS graphical plan and Plan Explorer cannot display the contents of the Value Vector, so we have to dig into the XML again.&amp;#160; The function evaluates the DATEADD(GETDATE()) expression, computes the open-interval start point of a DATE range accounting for the conversion from DATETIME to DATE, and specifies NULL as the end of the range (since this is a &amp;gt;= comparison, there is no end value).&amp;#160; The flags value in this case is 22 (the flags for a &amp;gt;= seek operation).&lt;/p&gt;

&lt;h3&gt;Everything All At Once&lt;/h3&gt;

&lt;p&gt;This last example features all sorts type sloppiness, resulting in an execution plan that uses &lt;strong&gt;GetRangeThroughConvert&lt;/strong&gt; on the string expression and &lt;strong&gt;GetRangeThroughConvert&lt;/strong&gt; &lt;em&gt;on the result of&lt;/em&gt; &lt;strong&gt;GetRangeWithMismatchedTypes&lt;/strong&gt; applied to the result of the GETDATE function.&amp;#160; The whole thing is then wrapped in a dynamic seek with the Merge Interval enforcing the (annoying) BETWEEN requirement that the first parameter must be less than or equal to the second.&amp;#160; See if you can work out all the conversions necessary for this query, using the rules of data type precedence.&amp;#160; It is really quite impressive that this example of lazy T-SQL coding results in an index seek, don’t you think?&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DECLARE&lt;/span&gt; @Example &lt;span style="color:#0000ff;"&gt;TABLE&lt;/span&gt; (col1 DATETIME &lt;span style="color:#0000ff;"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;KEY&lt;/span&gt;)&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; * &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; @Example &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; e&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;WHERE&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;CONVERT&lt;/span&gt;(&lt;span style="color:#0000ff;"&gt;DATE&lt;/span&gt;, e.col1) &lt;span style="color:#0000ff;"&gt;BETWEEN&lt;/span&gt; &lt;span style="color:#006080;"&gt;'20000101'&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;AND&lt;/span&gt; GETDATE();&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_2BF7EE44.png" target="_blank"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="Merge Interval Implcit Conversions" border="0" alt="Merge Interval With Everything" src="http://sqlblog.com/blogs/paul_white/image_thumb_1AC410A1.png" width="651" height="450" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;SQL Server works quite hard sometimes to produce index seeks where they might seem unlikely.&amp;#160; This is a good thing, and it would be great to see this capability &lt;a href="http://connect.microsoft.com/SQLServer/feedback/details/526431/make-more-functions-sargable"&gt;extended further&lt;/a&gt; in future.&amp;#160; The downside is that this extra effort means you are less likely to see an Index Scan when you have done something daft with data types.&lt;/p&gt;

&lt;p&gt;Why is this a bad thing if you get a seek anyway?&amp;#160; The problem is that these hidden implicit conversions can result in inaccurate cardinality and distribution estimations at any stage of the plan.&amp;#160; So, even if you get a seek, the plan might be way off overall.&amp;#160; If that isn’t persuasive enough, consider this: will having hidden nested range calculations improve your chances of getting a good query plan?&amp;#160; Probably not, no.&amp;#160; Be very aware of types, and in particular of the types returned by functions and expressions.&amp;#160; If in doubt, use SELECT INTO to materialize the results of an expression or query, and check the types of the columns produced.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;

  &lt;hr /&gt;Note: if you have any scripts that trawl the plan cache looking for implicit conversions (CONVERT_IMPLICIT), you might want to look into updating them to check for these conversions too.&amp;#160; Not all conversions are bad ones, of course :) 

  &lt;br /&gt;

  &lt;hr /&gt;&lt;/blockquote&gt;

&lt;p&gt;© 2012 Paul White 
  &lt;br /&gt;Twitter: &lt;a href="http://twitter.com/SQL_Kiwi"&gt;@SQL_Kiwi&lt;/a&gt; 

  &lt;br /&gt;Email: &lt;a href="mailto:SQLkiwi@gmail.com"&gt;SQLkiwi@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Further Reading:&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://blogs.msdn.com/b/craigfr/archive/2008/06/05/implicit-conversions.aspx"&gt;Implicit Conversions&lt;/a&gt; – Craig Freedman 

  &lt;br /&gt;&lt;a href="http://blogs.msdn.com/b/craigfr/archive/2010/01/20/more-on-implicit-conversions.aspx"&gt;More on Implicit Conversions&lt;/a&gt; – Craig Freedman 

  &lt;br /&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/07/19/join-performance-implicit-conversions-and-residuals.aspx" target="_blank"&gt;Join Performance, Implicit Conversions, and Residuals&lt;/a&gt; - Me&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=41097" width="1" height="1"&gt;</description></item><item><title>Forcing a Parallel Query Execution Plan</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/12/23/forcing-a-parallel-query-execution-plan.aspx</link><pubDate>Thu, 22 Dec 2011 21:09:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:40596</guid><dc:creator>Paul White</dc:creator><slash:comments>24</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/40596.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=40596</wfw:commentRss><description>&lt;p align="left"&gt;&lt;font color="#000000"&gt;This post is for SQL Server developers who have experienced the special kind of frustration, which only comes from spending hours trying to convince the query optimizer to generate a parallel execution plan.&amp;#160; This situation often occurs when making an apparently innocuous change to the text of a moderately complex query; a change which somehow manages to turn a parallel plan that executes in ten seconds, into a five-minute serially-executing monster.&lt;/font&gt;&lt;/p&gt;  &lt;p align="left"&gt;SQL Server provides a number of query and table hints that allow the experienced practitioner to take greater control over the final form of a query plan.&amp;#160; These hints are usually seen as a tool of last resort, because they can make code harder to maintain, introduce extra dependencies, and may prevent the optimizer reacting to future changes in indexing or data distribution.&amp;#160; One such query hint is the (over) popular &lt;font face="Courier New"&gt;OPTION (MAXDOP 1)&lt;/font&gt;, which prevents the optimizer from considering plans that use parallelism.&amp;#160; Sadly, there is currently no corresponding hint to force the optimizer to choose a parallel plan.&lt;/p&gt;  &lt;p align="left"&gt;The result of all this is a great deal of wasted time trying increasingly obscure query syntax, until eventually the desired parallel plan is obtained, or the developer gives up in despair.&amp;#160; Even where success is achieved, the price is often fragile code that risks reverting to serial execution any time indexing or statistics change.&amp;#160; In any case, the resulting SQL is usually hard to read, and scary to maintain.&lt;/p&gt;  &lt;h2&gt;Why Expensive Queries Produce Serial Plans&lt;/h2&gt;  &lt;p align="left"&gt;Whenever the query optimizer produces a serial plan instead of the ‘obviously better’ parallel plan, there is always a reason.&amp;#160; Leaving aside the more obvious causes, such as the configuration setting &lt;a href="http://msdn.microsoft.com/en-us/library/ms181007.aspx" target="_blank"&gt;max degree of parallelism&lt;/a&gt; being set to one, running under a Resource Governor &lt;a href="http://msdn.microsoft.com/en-us/library/bb934146.aspx" target="_blank"&gt;workload group&lt;/a&gt; with &lt;font face="Courier New"&gt;MAX_DOP&lt;/font&gt;&lt;font face="Verdana"&gt; = 1, or having only one logical processor &lt;a href="http://msdn.microsoft.com/en-us/library/ee210585.aspx" target="_blank"&gt;available&lt;/a&gt; to SQL Server, the usual causes of a serial plan are parallelism-inhibiting operations, cardinality estimation errors, costing model limitations, and code path issues.&lt;/font&gt;&lt;/p&gt;  &lt;h3&gt;Parallelism-Inhibiting Components&lt;/h3&gt;  &lt;p align="left"&gt;There are many things that prevent parallelism, either because they make no sense in a parallel plan, or because the product just does not support them yet.&amp;#160; Some of these force the whole plan to run serially, others require a ‘serial zone’ – a part of the plan that runs serially, even though other parts may use multiple threads concurrently.&lt;/p&gt;  &lt;p&gt;That list changes from version to version, but for example these things make the whole plan serial on SQL Server 2008 R2 SP1:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Modifying the contents of a table variable (reading is fine) &lt;/li&gt;    &lt;li&gt;Any T-SQL scalar function (which are evil anyway) &lt;/li&gt;    &lt;li&gt;CLR scalar functions marked as performing data access (normal ones are fine) &lt;/li&gt;    &lt;li&gt;Random intrinsic functions including &lt;font face="Courier New"&gt;OBJECT_NAME&lt;/font&gt;, &lt;font face="Courier New"&gt;ENCYPTBYCERT&lt;/font&gt;, and &lt;font face="Courier New"&gt;IDENT_CURRENT&lt;/font&gt; &lt;/li&gt;    &lt;li&gt;System table access (e.g. sys.tables) &lt;/li&gt; &lt;/ul&gt;  &lt;p align="left"&gt;Inconveniently, the list of intrinsic functions is quite long and does not seem to follow a pattern.&amp;#160; &lt;font face="Courier New"&gt;ERROR_NUMBER&lt;/font&gt; and &lt;font face="Courier New"&gt;@@TRANCOUNT&lt;/font&gt;&lt;font face="Verdana"&gt; &lt;/font&gt;also force a serial plan, &lt;font face="Courier New"&gt;@@ERROR&lt;/font&gt;&lt;font face="Verdana"&gt; and &lt;font face="Courier New"&gt;@@NESTLEVEL&lt;/font&gt; do not.&amp;#160; The T-SQL scalar function restriction is also a bit sneaky.&amp;#160; Any reference to a table with a computed column that uses such a function will result in a serial plan, even if the problematic column is not referenced in the query.&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;These query features are examples that require a serial zone in the plan:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;TOP &lt;/li&gt;    &lt;li&gt;Sequence project (e.g. &lt;font face="Courier New"&gt;ROW_NUMBER, RANK&lt;/font&gt;) &lt;/li&gt;    &lt;li&gt;Multi-statement T-SQL table-valued functions &lt;/li&gt;    &lt;li&gt;Backward range scans (forward is fine) &lt;/li&gt;    &lt;li&gt;Global scalar aggregates &lt;/li&gt;    &lt;li&gt;Common sub-expression spools &lt;/li&gt;    &lt;li&gt;Recursive CTEs &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;The information presented above is based on the &lt;a href="http://blogs.msdn.com/b/craigfr/archive/2007/04/17/parallel-query-execution-presentation.aspx"&gt;original list&lt;/a&gt; published by Craig Freedman, and updated for 2008 R2.&lt;/p&gt;  &lt;p align="left"&gt;One way to check that a query does not have any parallelism-inhibiting components is to test the query using a CPU cost multiplier.&amp;#160; This should only be done on a private test system where you are able to flush the whole plan cache after testing.&amp;#160; The idea is to use an undocumented and unsupported DBCC command to temporarily increase the CPU cost of the query plan operators.&amp;#160; It is not a fool-proof test (some rare parallelizable queries will not generate a parallel plan with this technique) but it is quite reliable:&lt;/p&gt;  &lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;   &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;     &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DBCC&lt;/span&gt; FREEPROCCACHE&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DBCC&lt;/span&gt; SETCPUWEIGHT(1000)&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;GO&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#008000;"&gt;-- Query to test&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    COUNT_BIG(*) &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;LEFT&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt; &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.ProductID = th.ProductID&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;GO&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DBCC&lt;/span&gt; SETCPUWEIGHT(1)&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;DBCC&lt;/span&gt; FREEPROCCACHE&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The final commands to reset the CPU weighting factor and flush the plan cache are very important.&lt;/p&gt;

&lt;p align="left"&gt;If you get an parallel estimated plan for a particular test query, it shows that a parallel plan is at least possible.&amp;#160; Varying the value passed to the DBCC command adjusts the multiplier applied to normal CPU costs, so you will likely see different plans for different values.&amp;#160; The illustrated factor of a thousand is often enough to produce a parallel estimated plan, but you may need to experiment with higher values.&amp;#160; It is not recommended to use estimated plans obtained using this technique directly in USE PLAN hints or plan guides because these are not plans the optimizer would ever produce naturally.&amp;#160; To be clear, direct use of the plans would likely render a production system unsupported and the person responsible might be fired, shot, or possibly both.&lt;/p&gt;

&lt;h3&gt;Cardinality Estimation Errors&lt;/h3&gt;

&lt;p align="left"&gt;If there is nothing that absolutely prevents parallelism in the target query, the optimizer may still choose a serial alternative if it has a lower estimated cost.&amp;#160; For that reason, there are a couple of things we can do to promote the parallel option here, all based on the very sound notion of giving the optimizer accurate information to base its estimates on.&amp;#160; The considerations here go well beyond just ensuring statistics are up-to-date, or building them with the &lt;font face="Courier New"&gt;FULLSCAN&lt;/font&gt; option.&amp;#160; For example, depending on the nature of the query, you may need to provide all or some of the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Multi-column statistics (for correlations) &lt;/li&gt;

  &lt;li&gt;Filtered statistics or indexes (for more histogram steps) &lt;/li&gt;

  &lt;li&gt;Computed columns on filtering expressions in the query (avoid cardinality guesses) &lt;/li&gt;

  &lt;li&gt;Good constraint information (foreign keys and check constraints) &lt;/li&gt;

  &lt;li&gt;Materialize parts of the query in temporary tables (more accurate statistics in deep plans) &lt;/li&gt;

  &lt;li&gt;Regular hints such as &lt;font face="Courier New"&gt;OPTIMIZE FOR&lt;/font&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p align="left"&gt;In general, anything you can do to ensure that estimated row counts are close to the runtime values will help the optimizer cost the serial and parallel alternatives more accurately.&amp;#160; Many failures to choose a parallel plan are caused by inaccurate row counts.&lt;/p&gt;

&lt;h3&gt;Model Limitations&lt;/h3&gt;

&lt;p align="left"&gt;SQL Server uses a model to estimate the runtime cost of each operator in a query plan.&amp;#160; The exact calculations vary between operators, but most are based on a minimum cost, with an additional per-row component.&amp;#160; None of these estimates of expected CPU and I/O cost take into account the specific hardware SQL Server finds itself running on.&amp;#160; The advantage of this is that plans from one machine can be readily reproduced and compared on another machine running the same version of the software, without having to worry about hardware differences.&lt;/p&gt;

&lt;p align="left"&gt;Not all operators can be costed reasonably, and things like functions are particularly problematic because the optimizer has no clue how many rows might be produced or what the distribution of values might look like.&amp;#160; Even very normal-looking operators can pose problems.&amp;#160; Consider the task of estimating the number of rows and distribution of values resulting from a join or a complex GROUP BY clause.&amp;#160; Even where reasonable estimates can be made, the derived statistics that propagate up the query tree (from the persistent statistics at the leaves) tend to become quickly less reliable.&amp;#160; The optimizer includes many heuristics that aim to prevent these inaccuracies getting out of control, so it might resort to complete guesses after only a few operators as the compounding effect of deriving new statistics takes hold.&lt;/p&gt;

&lt;p align="left"&gt;There are many other assumptions and limitations of the model that will not fit into a blog post, the interested reader can find more detailed information in Chapter 8 of the indispensable SQL Server 2008 Internals book.&lt;/p&gt;

&lt;h3 align="left"&gt;Costing Limitations&lt;/h3&gt;

&lt;p align="left"&gt;When SQL Server costs a parallel plan, it generally reduces the CPU cost for a parallel iterator by the a factor equal to the expected runtime DOP.&amp;#160; For example the previous query can produce the following serial and parallel plans:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_65F95FCE.png"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_2867EE06.png" width="483" height="217" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_5F61CC3F.png"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_1D7AE6F1.png" width="660" height="203" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;Taking the Merge Join operator as an example, the parallel version has its CPU cost reduced by a factor of 4 when the expected runtime DOP is four (serial plan on the left, parallel on the right):&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_261B3F7D.png"&gt;&lt;img style="background-image:none;border-right-width:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_7925DCA1.png" width="446" height="258" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;On the other hand, the Index Scans show no reduction in I/O cost, though the CPU cost is again reduced by a factor of four:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_0951A49B.png"&gt;&lt;img style="background-image:none;border-right-width:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_2E6EEF07.png" width="546" height="306" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;As mentioned earlier, different operators cost themselves differently (for example a many-to-many merge join also has an I/O cost component that also happens to be reduced by a factor of four).&amp;#160; These details also vary somewhat between releases, so the presentation here is to give you an appreciation of the general approach rather than to dwell too much on the specifics.&lt;/p&gt;

&lt;p align="left"&gt;Looking again at the serial and parallel plans, it is clear that which of the two plans costs cheaper depends on whether the parallel plan saves enough by reducing CPU and I/O costs in the various operators, to pay for the extra operators in the plan.&amp;#160; In this case, the extra operators are three exchange (parallelism) operators – two Repartition Streams to redistribute rows for correct results when joining, and one Gather Streams to merge the threads back to a single final result.&lt;/p&gt;

&lt;p align="left"&gt;The way the numbers work means that it is often a tight race between the best parallel and serial plan alternatives.&amp;#160; In many real-world cases, the difference between the two can be extremely small – making it even more frustrating when the serial version turns out to take fifty times as long as the parallel version to execute.&amp;#160; One other point worth mentioning again here is that the DOP estimate is limited to the number of logical processors that SQL Server sees, divided by two.&amp;#160; My test machine has eight cores, all available to SQL Server, but the DOP estimate used for costing calculations is limited to four.&amp;#160; This has obvious consequences for costing, where CPU and I/O costs are typically divided by the estimated DOP of four, rather than eight.&lt;/p&gt;

&lt;h4 align="left"&gt;A Note about Parallel Nested Loops&lt;/h4&gt;

&lt;p align="left"&gt;Plans with Nested Loops joins can be a particular problem, because the inner side almost always runs multiple threads serially.&amp;#160; The parallelism icons are still present, but they indicate that there are DOP independent serial threads.&amp;#160; The distinction is perhaps a subtle one, but it (a) explains why operators that normally force a serial zone can run ‘in parallel’ on the inner side of a loops join; and (b) the optimizer does not reduce the CPU costs on the inner side by the estimated runtime DOP.&amp;#160; This puts nested loops at an unfair disadvantage when it comes to parallelism costing, compared with Hash and Merge Joins.&lt;/p&gt;

&lt;h3&gt;Code Path Issues&lt;/h3&gt;

&lt;p align="left"&gt;This last category concerns the fact that the optimizer may not get as far as evaluating a parallel plan at all.&amp;#160; One way this can occur is if a final plan is found during the Trivial Plan stage.&amp;#160; If a Trivial Plan is possible, &lt;b&gt;and&lt;/b&gt; the resulting cost is less than the configured cost threshold for parallelism, the full optimization stages are skipped and a serial plan is returned immediately.&lt;/p&gt;

&lt;h4&gt;Trivial Plan&lt;/h4&gt;

&lt;p align="left"&gt;The following query has an estimated serial plan cost of around 85 units, but with the parallelism threshold set to 100 a Trivial Plan is produced (as shown in the plan property ‘Optimization Level’ or by checking the changes in &lt;a href="http://technet.microsoft.com/en-us/library/ms175002.aspx"&gt;sys.dm_exec_query_optimizer_info&lt;/a&gt; as shown below:&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    deqoi.[counter],&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    deqoi.occurrence&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; sys.dm_exec_query_optimizer_info &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; deqoi &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;WHERE&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    [counter] &lt;span style="color:#0000ff;"&gt;IN&lt;/span&gt; (&lt;span style="color:#006080;"&gt;'trivial plan'&lt;/span&gt;, &lt;span style="color:#006080;"&gt;'search 0'&lt;/span&gt;, &lt;span style="color:#006080;"&gt;'search 1'&lt;/span&gt;, &lt;span style="color:#006080;"&gt;'search 2'&lt;/span&gt;)&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;GO&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SET&lt;/span&gt; SHOWPLAN_XML &lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;GO&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    COUNT_BIG(*) &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; dbo.bigTransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; bth&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;OPTION&lt;/span&gt; (RECOMPILE)&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;GO&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SET&lt;/span&gt; SHOWPLAN_XML &lt;span style="color:#0000ff;"&gt;OFF&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;GO&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    deqoi.[counter],&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    deqoi.occurrence&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; sys.dm_exec_query_optimizer_info &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; deqoi &lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;WHERE&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    [counter] &lt;span style="color:#0000ff;"&gt;IN&lt;/span&gt; (&lt;span style="color:#006080;"&gt;'trivial plan'&lt;/span&gt;, &lt;span style="color:#006080;"&gt;'search 0'&lt;/span&gt;, &lt;span style="color:#006080;"&gt;'search 1'&lt;/span&gt;, &lt;span style="color:#006080;"&gt;'search 2'&lt;/span&gt;)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_3E9AB700.png"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_4ABC3127.png" width="359" height="220" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the cost threshold is reduced to 84 we get a parallel plan…&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_41EC28DB.png"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_2DF29645.png" width="552" height="220" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;A deeper analysis shows that the query still qualified for Trivial Plan (and the stage was run) but the final cost exceeded the parallelism threshold so optimization continued.&amp;#160; This query does not qualify for ‘search 0’ (TP or Transaction Processing) because a minimum of three tables are required there.&lt;/p&gt;

&lt;p align="left"&gt;So, optimization moves on to ‘search 1’ (Quick Plan) which runs twice.&amp;#160; It runs once considering only serial plans, and comes out with a best cost of 84.6181.&amp;#160; Since this exceeds the threshold of 84, Quick Plan is re-run with the parallel plan option enabled.&amp;#160; The result is a parallel plan at cost 44.7854.&amp;#160; The plan does not meet the entry conditions for ‘search 2’ (Full Optimization) so the finished plan is copied out.&lt;/p&gt;

&lt;h4&gt;Good Enough Plan &amp;amp; Time Out&lt;/h4&gt;

&lt;p align="left"&gt;Returning to code path reasons that prevent a parallel plan, the last category covers queries that enter the Quick Plan stage, but that stage terminates early, either with a Good Enough Plan Found message, or a Time Out.&amp;#160; Both of these are heuristics to prevent the optimizer spending more time optimizing than it stands to gain by reducing estimated execution time (cost).&amp;#160; Good Enough Plan results when the current lowest cost plan is so cheap that further optimization effort is no longer justified.&lt;/p&gt;

&lt;p align="left"&gt;Time Out is a related phenomenon: at the start of a stage, the optimizer sets itself a ‘budget’ of a number of rule applications it estimates it can perform in the time justified by the initial cost of the plan.&amp;#160; This means that query trees that start with a higher cost get a correspondingly larger allowance of rule applications (roughly comparable to the number of moves a chess program thinks ahead).&amp;#160; If the optimizer explores the allowed number of rules before the natural end of the optimization stage, it returns the best complete plan at that point with a Time Out message.&amp;#160; This may well occur during the first run of ‘search 1’, preventing us reaching the second run that adds parallelism.&lt;/p&gt;

&lt;p align="left"&gt;One interesting consequence of the rule concerning Trivial Plan and the cost threshold for parallelism is that &lt;i&gt;a system configured with a threshold of zero can never produce a Trivial Plan&lt;/i&gt;.&amp;#160; Bearing this in mind, we can generate a surprising Time Out with this query:&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; * &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;As you would expect, this query is normally optimized using Trivial Plan (there are no cost-based plan choices here):&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_3E1E5E3E.png"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_1128FB63.png" width="560" height="325" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;…but when the cost threshold is set to zero, we get Full Optimization with a Time Out…the optimizer timed out working out how to do SELECT * from a single table!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_2154C35C.png"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_46720DC8.png" width="560" height="337" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this particular case, the optimizer ‘timed out’ after 15 tasks (it normally runs through many thousands).&amp;#160; A Time Out result can sometimes also be an indicator that the input query is over-complex, but the interpretation is not at all that straightforward.&lt;/p&gt;

&lt;h2&gt;The Solution&lt;/h2&gt;

&lt;p align="left"&gt;We need a robust query plan hint, analogous to MAXDOP, that we can specify as a last resort when all other techniques still result in a serial plan, and where the parallel alternative is much to be preferred of course.&amp;#160; I really want to emphasise that very many cases of unwanted serial plans are due to designers and developers not giving the optimizer good quality information.&amp;#160; I see very few systems with things like proper multi-column statistics, filtered indexes/statistics, and adequate constraints.&amp;#160; Even less frequently, do I see (perhaps non-persisted) computed columns created on query filter expressions to aid cardinality estimation.&amp;#160; On the other hand, non-relational database designs with poor indexing, and decidedly non-relational queries are extremely common.&amp;#160; (As are database developers complaining about the poor decisions the optimizer makes sometimes!)&lt;/p&gt;

&lt;h3 align="left"&gt;There’s always a Trace Flag&lt;/h3&gt;

&lt;p align="left"&gt;In the meantime, there is a workaround.&amp;#160; It’s not perfect (and most certainly a choice of very last resort) but there is an undocumented (and unsupported) trace flag that effectively lowers the cost threshold to zero for a particular query.&amp;#160; It actually goes a little further than that; for example, the following query will not generate a parallel plan even with a zero cost threshold:&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;TOP&lt;/span&gt; (1)&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    th.ProductID = p.ProductID&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;ORDER&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;BY&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_569DD5C1.png"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_22F56963.png" width="500" height="219" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a completely trivial query plan of course – the first row from the scan is joined to a single row from the seek.&amp;#160; The total estimated cost of the serial plan is 0.0065893.&amp;#160; Returning the cost threshold for parallelism to the default of 5 just for completeness, we can obtain a parallel plan (purely for demonstration purposes) using the trace flag:&lt;/p&gt;

&lt;div style="border-bottom:silver 1px solid;text-align:left;border-left:silver 1px solid;padding-bottom:4px;line-height:12pt;background-color:#f4f4f4;margin:20px 0px 10px;padding-left:4px;width:97.5%;padding-right:4px;font-family:'Courier New', courier, monospace;direction:ltr;max-height:200px;font-size:8pt;overflow:auto;border-top:silver 1px solid;cursor:text;border-right:silver 1px solid;padding-top:4px;" id="codeSnippetWrapper"&gt;
  &lt;div style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;" id="codeSnippet"&gt;
    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;TOP&lt;/span&gt; (1)&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    th.ProductID = p.ProductID&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;ORDER&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;BY&lt;/span&gt;&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:white;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;    p.Name&lt;/pre&gt;


    &lt;pre style="border-bottom-style:none;text-align:left;padding-bottom:0px;line-height:12pt;background-color:#f4f4f4;margin:0em;border-left-style:none;padding-left:0px;width:100%;padding-right:0px;font-family:'Courier New', courier, monospace;direction:ltr;border-top-style:none;color:black;border-right-style:none;font-size:8pt;overflow:visible;padding-top:0px;"&gt;&lt;span style="color:#0000ff;"&gt;OPTION&lt;/span&gt; (RECOMPILE, QUERYTRACEON 8649)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_4812B3CF.png"&gt;&lt;img style="background-image:none;border-right-width:0px;padding-left:0px;padding-right:0px;display:inline;border-top-width:0px;border-bottom-width:0px;border-left-width:0px;padding-top:0px;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/paul_white/image_thumb_3F42AB83.png" width="613" height="216" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The parallel alternative is returned, despite the fact it costs much higher at 0.0349929 (5.3 times the cost of the serial plan).&amp;#160; In my testing, this trace flag has proved invaluable in certain particularly tricky cases where a parallel plan is essential, but there is no reasonable way to get it from the standard optimizer.&lt;/p&gt;

&lt;h3 align="left"&gt;Conclusion&lt;/h3&gt;

&lt;p align="left"&gt;Even experts with decades of SQL Server experience and detailed internal knowledge will want to be careful with this trace flag.&amp;#160; I cannot recommend you use it directly in production unless advised by Microsoft, but you might like to use it on a test system as an extreme last resort, perhaps to generate a plan guide or USE PLAN hint for use in production (after careful review).&lt;/p&gt;

&lt;p align="left"&gt;This is an arguably lower risk strategy, but bear in mind that the parallel plans produced under this trace flag are not guaranteed to be ones the optimizer would normally consider.&amp;#160; If you can improve the quality of information provided to the optimizer instead to get a parallel plan, go that way :)&lt;/p&gt;

&lt;p align="left"&gt;If you would prefer to see a fully supported T-SQL OPTION (MINDOP) or OPTION (PARALLEL_PLAN) hint, please vote here:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="https://connect.microsoft.com/SQLServer/feedback/details/714968/provide-a-hint-to-force-generation-of-a-parallel-plan" target="_blank"&gt;https://connect.microsoft.com/SQLServer/feedback/details/714968/provide-a-hint-to-force-generation-of-a-parallel-plan&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;© 2011 Paul White 
  &lt;br /&gt;Twitter: &lt;a href="http://twitter.com/SQL_Kiwi"&gt;@SQL_Kiwi&lt;/a&gt; 

  &lt;br /&gt;Email: &lt;a href="mailto:SQLkiwi@gmail.com"&gt;SQLkiwi@gmail.com&lt;/a&gt;&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=40596" width="1" height="1"&gt;</description></item><item><title>SQL Server Optimizer Bug with JOIN and GROUP BY</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/12/06/sql-server-optimizer-bug-with-join-and-group-by.aspx</link><pubDate>Mon, 05 Dec 2011 23:17:58 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:40150</guid><dc:creator>Paul White</dc:creator><slash:comments>6</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/40150.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=40150</wfw:commentRss><description>I came across a SQL Server bug recently that made me wonder how on earth I never noticed it before.&amp;#160; As the title of this post suggests, the bug occurs in common JOIN and GROUP BY queries, and while it does not cause incorrect results to be returned,...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/12/06/sql-server-optimizer-bug-with-join-and-group-by.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=40150" width="1" height="1"&gt;</description></item><item><title>Is Distinct Aggregation Still Considered Harmful?</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/12/04/is-distinct-aggregation-still-considered-harmful.aspx</link><pubDate>Sat, 03 Dec 2011 18:30:43 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:40125</guid><dc:creator>Paul White</dc:creator><slash:comments>9</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/40125.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=40125</wfw:commentRss><description>Back in 2008, Marc Friedman of the SQL Server Query Processor Team wrote a blog entry entitled “Distinct Aggregation Considered Harmful” , in which he shows a way to work around the poor performance that often results simply from adding the keyword DISTINCT...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/12/04/is-distinct-aggregation-still-considered-harmful.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=40125" width="1" height="1"&gt;</description></item><item><title>How to Find the Statistics Used to Compile an Execution Plan</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/09/21/how-to-find-the-statistics-used-to-compile-an-execution-plan.aspx</link><pubDate>Tue, 20 Sep 2011 15:22:15 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:38595</guid><dc:creator>Paul White</dc:creator><slash:comments>17</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/38595.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=38595</wfw:commentRss><description>In this post, I show you how to determine exactly which statistics objects were used by the query optimizer to produce an execution plan. Trace Flags We will need three undocumented trace flags.&amp;#160; The first one ( 3604 ) is well-known – it redirects...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/09/21/how-to-find-the-statistics-used-to-compile-an-execution-plan.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=38595" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Query+Optimizer/default.aspx">Query Optimizer</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Query+Plans/default.aspx">Query Plans</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Undocumented/default.aspx">Undocumented</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Trace+Flags/default.aspx">Trace Flags</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Statistics/default.aspx">Statistics</category></item><item><title>Can a SELECT query cause page splits?</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/08/30/can-a-select-query-cause-page-splits.aspx</link><pubDate>Mon, 29 Aug 2011 17:46:15 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:38127</guid><dc:creator>Paul White</dc:creator><slash:comments>7</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/38127.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=38127</wfw:commentRss><description>Books Online has this to say about page splits: When a new row is added to a full index page , the Database Engine moves approximately half the rows to a new page to make room for the new row.&amp;#160; This reorganization is known as a page split .&amp;#160;...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/08/30/can-a-select-query-cause-page-splits.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=38127" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Internals/default.aspx">Internals</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Spool/default.aspx">Spool</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Query+Plans/default.aspx">Query Plans</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Undocumented/default.aspx">Undocumented</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Memory+Grant/default.aspx">Memory Grant</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Sorting/default.aspx">Sorting</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Indexes/default.aspx">Indexes</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Parallelism/default.aspx">Parallelism</category></item><item><title>SQL Server, Seeks, and Binary Search</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/08/09/sql-server-seeks-and-binary-search.aspx</link><pubDate>Mon, 08 Aug 2011 20:10:27 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:37622</guid><dc:creator>Paul White</dc:creator><slash:comments>9</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/37622.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=37622</wfw:commentRss><description>The following table summarizes the results from my last two blog entries, showing the CPU time used when performing 5 million clustered index seeks: In test 1, making the clustered index unique improved performance by around 40%. In test 2, making the...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/08/09/sql-server-seeks-and-binary-search.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=37622" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Internals/default.aspx">Internals</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Undocumented/default.aspx">Undocumented</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Indexes/default.aspx">Indexes</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Seeks/default.aspx">Seeks</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Performance/default.aspx">Performance</category></item><item><title>Avoiding Uniqueness for Performance</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/08/04/avoiding-uniqueness-for-performance.aspx</link><pubDate>Wed, 03 Aug 2011 12:34:41 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:37452</guid><dc:creator>Paul White</dc:creator><slash:comments>15</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/37452.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=37452</wfw:commentRss><description>In my last post , I showed how using a index unique could speed up equality seeks by around 40%. For today’s entry, I’m going to use the same tables as last time (single BIGINT column, one table with a non-unique clustered index, and one table with a...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/08/04/avoiding-uniqueness-for-performance.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=37452" width="1" height="1"&gt;</description></item><item><title>Enforcing Uniqueness for Performance</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/07/29/enforcing-uniqueness-for-performance.aspx</link><pubDate>Thu, 28 Jul 2011 15:38:43 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:37377</guid><dc:creator>Paul White</dc:creator><slash:comments>7</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/37377.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=37377</wfw:commentRss><description>A little while back, I posted a short series on seeks and scans, and one of the things I highlighted was the difference between a singleton seek and a range scan.&amp;#160; You can find that post here , if you want a refresher. Anyway, the broad point is...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/07/29/enforcing-uniqueness-for-performance.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=37377" width="1" height="1"&gt;</description></item><item><title>Join Performance, Implicit Conversions, and Residuals</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/07/19/join-performance-implicit-conversions-and-residuals.aspx</link><pubDate>Mon, 18 Jul 2011 11:17:21 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:37014</guid><dc:creator>Paul White</dc:creator><slash:comments>14</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/37014.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=37014</wfw:commentRss><description>You probably already know that it’s important to be aware of data types when writing queries, and that implicit conversions between types can lead to poor query performance.&amp;#160; Some people have gone so far as to write scripts to search the plan cache...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/07/19/join-performance-implicit-conversions-and-residuals.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=37014" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Internals/default.aspx">Internals</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Query+Plans/default.aspx">Query Plans</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Undocumented/default.aspx">Undocumented</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Performance/default.aspx">Performance</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Joins/default.aspx">Joins</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Conversions/default.aspx">Conversions</category></item><item><title>Bitmap Magic (or… how SQL Server uses bitmap filters)</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/07/07/bitmap-magic.aspx</link><pubDate>Wed, 06 Jul 2011 15:44:15 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:36673</guid><dc:creator>Paul White</dc:creator><slash:comments>10</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/36673.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=36673</wfw:commentRss><description>Question: Can a parallel query use less CPU than the same serial query, while executing faster? The answer is yes; and to demonstrate, I'll use the following two (heap) tables, each containing a single column typed as INTEGER: Let’s load the first table...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/07/07/bitmap-magic.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=36673" width="1" height="1"&gt;</description></item><item><title>Undocumented Query Plans: The ANY Aggregate</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/07/02/undocumented-query-plans-the-any-aggregate.aspx</link><pubDate>Fri, 01 Jul 2011 14:51:56 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:36570</guid><dc:creator>Paul White</dc:creator><slash:comments>11</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/36570.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=36570</wfw:commentRss><description>As usual, here’s a sample table: With some sample data: And an index that will be useful shortly: There’s a complete script to create the table and add the data at the end of this post.&amp;#160; There’s nothing special about the table or the data (except...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/07/02/undocumented-query-plans-the-any-aggregate.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=36570" width="1" height="1"&gt;</description></item><item><title>Undocumented Query Plans: Equality Comparisons</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/06/22/undocumented-query-plans-equality-comparisons.aspx</link><pubDate>Tue, 21 Jun 2011 15:58:46 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:36392</guid><dc:creator>Paul White</dc:creator><slash:comments>22</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/36392.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=36392</wfw:commentRss><description>The diagram below shows two data sets, with differences highlighted: To find changed rows using TSQL, we might write a query like this: The logic is clear: join rows from the two sets together on the primary key column, and return rows where a change...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/06/22/undocumented-query-plans-equality-comparisons.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=36392" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Internals/default.aspx">Internals</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Query+Plans/default.aspx">Query Plans</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Undocumented/default.aspx">Undocumented</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/TSQL/default.aspx">TSQL</category></item><item><title>How Parallelism Works in SQL Server</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/03/05/how-parallelism-works-in-sql-server.aspx</link><pubDate>Sat, 05 Mar 2011 08:28:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:33919</guid><dc:creator>Paul White</dc:creator><slash:comments>7</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/33919.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=33919</wfw:commentRss><description>&lt;p&gt;You might have noticed that January was a quiet blogging month for me.&amp;nbsp; Part of the reason was that I was working on a series of articles for Simple Talk, examining how parallel query execution really works.&amp;nbsp; The first part is published today at:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.simple-talk.com/sql/learn-sql-server/understanding-and-using-parallelism-in-sql-server/"&gt;http://www.simple-talk.com/sql/learn-sql-server/understanding-and-using-parallelism-in-sql-server/&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;This introductory piece is not &lt;i&gt;quite&lt;/i&gt; as deeply technical as my SQLblog posts tend to be, but I hope there be enough interesting material to make it worth a read.&lt;/p&gt;  &lt;p&gt;Paul White   &lt;br&gt;email: &lt;a href="mailto:SQLkiwi@gmail.com"&gt;SQLkiwi@gmail.com&lt;/a&gt;    &lt;br&gt;twitter: &lt;a href="http://twitter.com/SQL_Kiwi"&gt;@SQL_Kiwi&lt;/a&gt;&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=33919" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Internals/default.aspx">Internals</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Parallelism/default.aspx">Parallelism</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/TSQL/default.aspx">TSQL</category></item><item><title>SQL Server Bug: Slow T-SQL Sums and Averages</title><link>http://sqlblog.com/blogs/paul_white/archive/2011/02/27/bug-slow-sums-and-averages.aspx</link><pubDate>Sun, 27 Feb 2011 00:07:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:33798</guid><dc:creator>Paul White</dc:creator><slash:comments>10</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/33798.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=33798</wfw:commentRss><description>It’s a curious thing about SQL that the SUM or AVG of no items is not zero, it’s NULL.  In this post, you’ll see how this means your SUM and AVG calculations might run at half speed, or worse.  As with most of my blog entries though, today’s instalment is not so much about the result, but the journey we take to get there.

Before we get started on that, I just want to mention that there’s a problem with the Google Reader feed for this blog, so those of you that use that will have missed two recent entries: Seeking Without Indexes and Advanced TSQL Tuning: Why Internals Knowledge Matters.  Accessing the site directly always works of course :)

Ok, on to today’s story.  Take a look at this query:...(&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2011/02/27/bug-slow-sums-and-averages.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=33798" width="1" height="1"&gt;</description></item></channel></rss>
