<?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>A technical SQL Server blog from New Zealand.</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>Parallel Execution Plans Suck</title><link>http://sqlblog.com/blogs/paul_white/archive/2012/05/03/parallel-row-goals-gone-rogue.aspx</link><pubDate>Wed, 02 May 2012 17:57:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:43147</guid><dc:creator>Paul White</dc:creator><slash:comments>7</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/43147.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=43147</wfw:commentRss><description>&lt;p align="left"&gt;&lt;i&gt;Summary: A deep dive into SQL Server parallelism, and a potential performance problem with parallel plans that use TOP.&lt;/i&gt;&lt;/p&gt;  &lt;p align="left"&gt;There was an interesting &lt;a href="http://dba.stackexchange.com/questions/7233/inaccurate-actual-row-counts-in-parallel-plan" target="_blank"&gt;question&lt;/a&gt; asked by &lt;a href="http://uk.linkedin.com/in/markstoreysmith" target="_blank"&gt;Mark Storey-Smith&lt;/a&gt; on &lt;a href="http://dba.stackexchange.com/"&gt;dba.stackexchange.com&lt;/a&gt; back in October 2011.&amp;#160; He was looking at the execution plan for a query that counts a million rows from a &lt;a href="http://www.sqlmag.com/article/sql-server/virtual-auxiliary-table-of-numbers"&gt;virtual auxiliary table of numbers&lt;/a&gt;.&amp;#160; The query below is a slightly-modified version of the one in the original post:&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:400px;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;WITH&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;    N1 (n) &lt;span style="color:#0000ff;"&gt;AS&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;/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;-- 10 rows&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1&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;/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;    N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;CROSS&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R), &lt;span style="color:#008000;"&gt;-- 100 rows&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;    N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;CROSS&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R), &lt;span style="color:#008000;"&gt;-- 10,000 rows&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;    N4 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;CROSS&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R), &lt;span style="color:#008000;"&gt;-- 100,000,000 rows&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;    N5 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N4 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;CROSS&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R), &lt;span style="color:#008000;"&gt;-- 1,000,000,000 rows&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;    N6 &lt;span style="color:#0000ff;"&gt;AS&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;/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;TOP&lt;/span&gt; (1000000) n&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; N5&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;/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; N6&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, MAXDOP 1);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;This particular virtual numbers table is capable of producing up to a (&lt;a href="http://en.wikipedia.org/wiki/Billion"&gt;short-scale&lt;/a&gt;) billion rows, via a number of cross joins, but the final common table expression N6 limits it to one million with the TOP clause.&amp;#160; The query plan is just a sequence of cross joins of the ten in-memory rows defined by the first common table expression, N1 (click to enlarge):&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_7B6BCF68.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="One million row serial quey plan" border="0" alt="One million row serial quey plan" src="http://sqlblog.com/blogs/paul_white/image_thumb_38AC8430.png" width="660" height="98" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;As usual, the Constant Scan operator is used to generate rows in memory without accessing a physical table, but this one has an interesting property: the ‘virtual table’ contains ten rows, but &lt;b&gt;no columns&lt;/b&gt;.&amp;#160; The query itself only counts rows, it does nothing at all with any column in those rows, and the optimizer contains logic to only project columns that are needed later in the query plan.&amp;#160; If you look at the query plan in SSMS or SQL Sentry Plan Explorer, you will see that the Constant Scans have a blank output columns list; they project no columns at all.&lt;/p&gt;

&lt;h3 align="left"&gt;Execution Plans Suck&lt;/h3&gt;

&lt;p align="left"&gt;The plan above does illustrate an important concept in plan-reading: &lt;b&gt;execution plans start executing at the left-most node&lt;/b&gt;.&amp;#160; People are often told to read execution plans from the top-right, and that is fine if you just want to follow the flow of data – so long as you bear in mind that the flow of program control starts at the root (far left).&lt;/p&gt;

&lt;p align="left"&gt;Rob Farley (&lt;a href="http://sqlblog.com/blogs/rob_farley"&gt;blog&lt;/a&gt; | &lt;a href="https://twitter.com/#!/rob_farley"&gt;twitter&lt;/a&gt;) sums this up by saying “execution plans suck”; a reference to the fact that rows are pulled up the tree by parent operators requesting rows, &lt;b&gt;one at a time&lt;/b&gt;, from the its child operator(s).&amp;#160; Query execution follows a &lt;b&gt;demand-based pipeline model&lt;/b&gt; (except in batch mode in parallel 2012 column-store plans, but that is a tangent we will not be pursuing today).&amp;#160; I’m not going to labour the point, but if you are interested to understand this better, take a look at my &lt;a href="http://sqlblog.com/blogs/paul_white/archive/2010/08/05/iterators-query-plans-and-why-they-run-backwards.aspx"&gt;previous post&lt;/a&gt; on the topic, or Brad Schulz’s &lt;a href="http://bradsruminations.blogspot.co.nz/2010/11/second-in-life-of-query-operator.html"&gt;entertaining overview&lt;/a&gt;.&lt;/p&gt;

&lt;p align="left"&gt;Let’s look at the execution plan (with runtime statistics) changing the TOP specification from one million to one hundred to make it easier to see what’s going on:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_4461CB62.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="TOP 100 Plan" border="0" alt="TOP 100 Plan" src="http://sqlblog.com/blogs/paul_white/image_thumb_6187736C.png" width="640" height="146" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;I’m just showing part of the plan for clarity.&amp;#160; There are another four Constant Scans off to the right that all produce one row.&amp;#160; If query plans really did start executing at the top right, we would expect one row from the right-most Constant Scan, ten rows from its parent, and one hundred at the next operator up the tree (working right to left).&amp;#160; As it is, the expected pattern (one, then ten, then one hundred) appears closest to the Top operator.&amp;#160; This only makes sense if a row at a time is sucked up the plan from the root.&amp;#160; The pipelined (row-by-row) model also explains why execution stops after 100 rows; the Top operator in the plan simply stops requesting a new row from its immediate child at that point.&lt;/p&gt;

&lt;h3 align="left"&gt;The Question&lt;/h3&gt;

&lt;p align="left"&gt;Back to the main thrust of today’s post.&amp;#160; The question arose when Mark ran his query with parallelism enabled:&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:400px;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;WITH&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;    N1 (n) &lt;span style="color:#0000ff;"&gt;AS&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;/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;-- 10 rows&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1&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;/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;    N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;CROSS&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R), &lt;span style="color:#008000;"&gt;-- 100 rows&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;    N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;CROSS&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R), &lt;span style="color:#008000;"&gt;-- 10,000 rows&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;    N4 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;CROSS&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R), &lt;span style="color:#008000;"&gt;-- 100,000,000 rows&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;    N5 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N4 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;CROSS&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R), &lt;span style="color:#008000;"&gt;-- 1,000,000,000 rows&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;    N6 &lt;span style="color:#0000ff;"&gt;AS&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;/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;TOP&lt;/span&gt; (1000000) n&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; N5&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;/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; N6&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, MAXDOP 2);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;That produces an actual execution plan like this (click to enlarge):&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_49A08C46.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="Parallel Top One Million" border="0" alt="Parallel Top One Million" src="http://sqlblog.com/blogs/paul_white/image_thumb_624FB389.png" width="644" height="66" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The question relates to the actual number of rows shown entering and leaving the Gather Streams exchange operator:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_388C3896.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="Gather Streams Exchange" border="0" alt="Gather Streams Exchange" src="http://sqlblog.com/blogs/paul_white/image_thumb_360306D8.png" width="367" height="88" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;As expected, one million rows leave the exchange operator, but the plan shows &lt;b&gt;1,004,588&lt;/b&gt; rows entering it.&amp;#160; So the question is, are the row counts wrong, or if not, where did the rows go?&lt;/p&gt;

&lt;h3 align="left"&gt;The Answer&lt;/h3&gt;

&lt;p align="left"&gt;As you may know, the answer lies in the fact that exchanges &lt;a href="http://www.simple-talk.com/sql/learn-sql-server/understanding-and-using-parallelism-in-sql-server/"&gt;contain buffers&lt;/a&gt;.&amp;#160; For efficiency reasons, rows are not passed across threads one by one as in the normal model discussed above.&amp;#160; Instead, producer threads (on the right side of the exchange operator) pack rows one at a time into packets, and push completed packets across the &lt;a href="http://blogs.msdn.com/b/craigfr/archive/2006/10/25/the-parallelism-operator-aka-exchange.aspx"&gt;exchange&lt;/a&gt;.&amp;#160; Consumer thread(s) on the left side of the exchange retrieve rows one at a time from the current packet on demand, re-establishing the demand-based pipeline model.&amp;#160; The internal class name for the packets, by the way, is Class eXchange PACKET – which gives us the familiar CXPACKET moniker.&lt;/p&gt;

&lt;p align="left"&gt;Exploring the detail at little more, we can see from the execution plan that the two independent producer threads to the right of the exchange pack a total of 499,225 + 505,363 = &lt;b&gt;1,004,588&lt;/b&gt; rows into packets, and the single consumer thread (thread zero) retrieves just the one million rows needed by the Top operator:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_30A54DC8.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="Per Thread Row Counts" border="0" alt="Per Thread Row Counts" src="http://sqlblog.com/blogs/paul_white/image_thumb_4064E2CC.png" width="457" height="116" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;So, the actual row counts shown on the execution plan are correct, and the ‘missing rows’ are accounted for by rows that were added to packets but not ultimately needed by the consumer.&amp;#160; After the one millionth row is passed by the Top to the Stream Aggregate (and remember all these things happen one row at a time), the next time the Top gets control, it starts the process of shutting down the pipeline below it, rather than asking the Gather Streams exchange for another row.&amp;#160; Glossing over the finer details a little, instead of the Top calling a &lt;strong&gt;GetRow&lt;/strong&gt;() method on the &lt;i&gt;Gather Streams&lt;/i&gt; iterator, it calls a &lt;strong&gt;Close&lt;/strong&gt;() method instead.&lt;/p&gt;

&lt;h3 align="left"&gt;SQL Server 2008+&lt;/h3&gt;

&lt;p align="left"&gt;On SQL Server 2005 that is the end of the story.&amp;#160; On SQL Server 2008 and later (including 2012), there is more.&amp;#160; Let’s run the query one more time, but this time with a degree of parallelism of three, rather than two.&amp;#160; Sometimes, we will get a plan that contains this sort of row count arrangement at the Gather Streams:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_6BC903C6.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="DOP 3 Gather Streams" border="0" alt="DOP 3 Gather Streams" src="http://sqlblog.com/blogs/paul_white/image_thumb_693FD208.png" width="354" height="95" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;Much the same as before, a few thousand extra rows are processed by the producer threads than are ultimately needed by the consumer.&amp;#160; That’s fine, of course, the threads in a parallel plan execute independently, so there are bound to be small timing differences that lead to this sort of effect.&amp;#160; Every so often, however, executing this query on SQL Server 2008 or above will produce a result like this:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_51C51DD7.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="Monster Row Count" border="0" alt="Monster Row Count" src="http://sqlblog.com/blogs/paul_white/image_thumb_4F3BEC19.png" width="363" height="92" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;Whoa.&amp;#160; Nearly &lt;b&gt;335 million rows&lt;/b&gt; entering the exchange – and the query now runs for &lt;b&gt;50 seconds&lt;/b&gt; or so, having run consistently for around &lt;b&gt;200ms&lt;/b&gt; previously.&amp;#160; Looking at the per-thread actual row counts is revealing:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_4CB2BA5B.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="Monster Per Thread" border="0" alt="Monster Per Thread" src="http://sqlblog.com/blogs/paul_white/image_thumb_7163D1D2.png" width="546" height="140" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;If everything were perfectly balanced, we might expect each of the three producer threads to process around 333,000 rows.&amp;#160; Indeed, thread 1 and thread 2 are in this ballpark area, but thread 3 ground through &lt;b&gt;334 million rows&lt;/b&gt; on its own!&amp;#160; I should mention that there is nothing special about thread 3 (you are as likely to find the huge row count on thread 1 or thread 2, the labels are arbitrary).&amp;#160; Indeed, the problem can occur on any or all threads, as a second example run at DOP 3 shows below:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_68279691.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="DOP 3 Second Example" border="0" alt="DOP 3 Second Example" src="http://sqlblog.com/blogs/paul_white/image_thumb_21CA307C.png" width="368" height="95" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_1F40FEBE.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="DOP 3 Per Thread Row Counts 2" border="0" alt="DOP 3 Per Thread Row Counts 2" src="http://sqlblog.com/blogs/paul_white/image_thumb_5CEDE67A.png" width="565" height="140" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;This time two threads went rogue, resulting in over &lt;b&gt;667 million rows&lt;/b&gt; being processed in a total execution time of &lt;b&gt;67 seconds&lt;/b&gt;.&lt;/p&gt;

&lt;h3 align="left"&gt;Parallelism Problems?&lt;/h3&gt;

&lt;p align="left"&gt;There are other parallelism (exchange) operators in the plan, though we have concentrated only on the final Gather Streams operator so far.&amp;#160; By the way, Gather Streams is also known as a &lt;b&gt;Start Parallelism&lt;/b&gt; operator – a name that might surprise you since it seems to mark the &lt;b&gt;end&lt;/b&gt; of parallelism in the plan, rather than the start.&amp;#160; Remember that execution plans suck.&amp;#160; The ‘final’ Gather Streams is in fact the first operator to start executing, and it is responsible for &lt;b&gt;starting the parallel workers&lt;/b&gt; and attaching them to the threads that were reserved for this plan at the very start of execution.&amp;#160; Anyway, I digress.&amp;#160; Let’s look at the next exchange in the plan – reading left to right of course.&amp;#160; This is a &lt;i&gt;Repartition Streams&lt;/i&gt; exchange operating in &lt;i&gt;Round Robin&lt;/i&gt; distribution mode:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_5A64B4BC.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="Repartition Streams" border="0" alt="Repartition Streams" src="http://sqlblog.com/blogs/paul_white/image_thumb_30A139C9.png" width="570" height="262" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;This shows that 1,000 rows arriving on the producer side of the exchange, and a total of 668 on the consumer side.&amp;#160; Not shown in the diagram above, the producer side has 340 rows on thread 1, and 330 rows on each of thread 2 and thread 3.&amp;#160; Note that these are &lt;b&gt;not the same threads&lt;/b&gt; as the ones we saw numbered the same way before.&amp;#160; The numbering scheme is restarted for each independent parallel zone in the plan (and zones are bounded by a parallelism operator of one sort or another).&amp;#160; Anyway, the point is that the rows are pretty evenly distributed on the producer side of the exchange.&lt;/p&gt;

&lt;p align="left"&gt;On the consumer side (row counts illustrated above), things are rather different.&amp;#160; Thread 1 (in this zone) processes 334 rows, thread 2 gets 333, and thread 3 gets only one.&amp;#160; Now these &lt;b&gt;are&lt;/b&gt; the same threads as shown in the 667 million row diagram.&amp;#160; I mentioned that &lt;b&gt;parallel zones&lt;/b&gt; are bordered by parallelism operators; the current zone is bordered by the Repartition Streams on its right side, and by the Gather Streams on its left.&amp;#160; The same three threads are &lt;b&gt;consumers&lt;/b&gt; at the &lt;i&gt;Repartition Streams&lt;/i&gt;, and &lt;b&gt;producers&lt;/b&gt; at the &lt;i&gt;Gather Streams&lt;/i&gt; operator:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_2E1E64BA.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="Consumer Producer Branch" border="0" alt="Consumer Producer Branch" src="http://sqlblog.com/blogs/paul_white/image_thumb_1E2F1FF6.png" width="660" height="205" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;There is a clear relationship between the thread row counts at the consumer side of the Repartition Streams (334, 333, and 1 row) and the row counts at the producer side of the Gather Streams (334 million, 333 million, 338,614 rows).&amp;#160; The two problematic threads have multiplied their row counts by a factor of one million – precisely the effect of the &lt;b&gt;six cross joins&lt;/b&gt; in this parallelism zone.&amp;#160; The Constant Scan virtual tables contain ten rows each, and multiplying by ten, six times over, gives a factor of one million.&lt;/p&gt;

&lt;h3 align="left"&gt;Rogue Row Goals&lt;/h3&gt;

&lt;p align="left"&gt;Thread 3 in the example above ends up processing 338,614 rows.&amp;#160; This number has no special significance, except it shows that this thread did not run this portion of the plan to completion.&amp;#160; If it had, the single row it started with would have ended up as one million rows by the time it had been cross-joined six times with the ten-row Constant Scan table.&lt;/p&gt;

&lt;p align="left"&gt;This is the &lt;b&gt;row goal&lt;/b&gt; in action (if you need a details on exactly what a row goal is, please see &lt;a href="http://sqlblog.com/blogs/paul_white/archive/2010/08/18/inside-the-optimiser-row-goals-in-depth.aspx"&gt;my previous post&lt;/a&gt;).&amp;#160; Essentially, though, things like a TOP operator or a FAST n hint set a row goal.&amp;#160; Plans normally run to completion, but row goals modify this, producing a plan that tries to optimize for a &lt;b&gt;certain number of rows&lt;/b&gt; rather than the full potential set.&amp;#160; The TOP operator is even more special.&amp;#160; As discussed briefly before, TOP can bring execution to an early end – instead of continuing to ask for rows from its child iterator, it calls the &lt;b&gt;Close()&lt;/b&gt; method instead.&amp;#160; This call filters down the tree, and execution comes to an early end.&amp;#160; (Strictly, row goals affect optimizer choices and plan costing rather than being associated with the early end of execution, but I like the phrase ‘rogue row goal’).&lt;/p&gt;

&lt;p align="left"&gt;So, thread 3 did not run to completion – it responded to the early Close() call and only processed 338 thousand of the one million rows it could have produced if left alone.&amp;#160; Threads 1 and 2 never received the Close() call, or chose to ignore it.&amp;#160; These two rogues went on to process their full potential row set – 667 million rows – despite the fact that the TOP had seen the million rows it needed and was waiting for operations elsewhere in the parallel plan to stop.&amp;#160; We can see this by looking at the &lt;a href="http://msdn.microsoft.com/en-us/library/ms188743.aspx"&gt;sys.dm_os_waiting_tasks&lt;/a&gt; DMV while the long-running query is executing:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_06DDBED6.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="e_waitPortClose" border="0" alt="e_waitPortClose" src="http://sqlblog.com/blogs/paul_white/image_thumb_1D505D5D.png" width="662" height="173" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The output is split across two lines for readability, and shows &lt;b&gt;execution context zero&lt;/b&gt; blocked on a &lt;b&gt;CXPACKET&lt;/b&gt; wait by both execution context 1 and execution context 3.&amp;#160; Execution context zero is always thread zero – the so-called coordinator thread that runs the serial part of the execution plan to the left of the left-most &lt;i&gt;Gather Streams&lt;/i&gt; operator.&amp;#160; Put another way, context zero always runs the part of the plan &lt;i&gt;before&lt;/i&gt; (reading left to right) the &lt;em&gt;Start Parallelism&lt;/em&gt; operator (and therefore always runs a serial plan).&lt;/p&gt;

&lt;p align="left"&gt;OK, so CXPACKET means the thread is involved in a parallelism-related wait.&amp;#160; The extra detail in the &lt;i&gt;resource_description&lt;/i&gt; column tells us that the wait is occurring at the node id 2.&amp;#160; Checking the execution plan, we see that node 2 is indeed the Gather Streams exchange:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_2905A48F.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="Node 2" border="0" alt="Node 2" src="http://sqlblog.com/blogs/paul_white/image_thumb_2E741533.png" width="365" height="102" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The wait type of &lt;i&gt;e_waitPortClose&lt;/i&gt; means the consumer is waiting for the port to close.&amp;#160; If you are wondering what a &lt;b&gt;port&lt;/b&gt; is in this context, I will just say the parallelism architecture is more complex than just &lt;b&gt;CXPACKET&lt;/b&gt; – the wiring includes a &lt;b&gt;CXPort&lt;/b&gt; class, a &lt;b&gt;CXPipe&lt;/b&gt; class, a &lt;b&gt;CXTransLocal&lt;/b&gt; (local transport) class and a &lt;b&gt;CXTransRemote&lt;/b&gt; class (for the Parallel Data Warehouse edition).&amp;#160; There is also a linked map structure that shows how the various pipes, ports, and transports connect together.&amp;#160; Closing the port is one step in shutting down part of a parallel plan which is running on the other side of an exchange (via a transport, pipe, and port).&amp;#160; The stack trace below shows thread zero waiting for the port to close:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_69057809.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="Stack Trace" border="0" alt="Stack Trace" src="http://sqlblog.com/blogs/paul_white/image_thumb_7FE44985.png" width="536" height="217" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The important things are that (a) this problem &lt;b&gt;does not occur&lt;/b&gt; in SQL Server 2005; and (b) a number of changes were made to the internal parallelism implementation in SQL Server 2008.&amp;#160; These changes seem to have introduced a bug, where the consumer can wait for the port to close, but one or more producers either ignore the request, or fail to check for it, and go on to process the whole potential result set instead of stopping early.&lt;/p&gt;

&lt;h3 align="left"&gt;Not Just Nested Loops&lt;/h3&gt;

&lt;p align="left"&gt;Fellow SQLblogger Joe Chang (&lt;a href="http://sqlblog.com/blogs/joe_chang/"&gt;blog&lt;/a&gt;) suggested in the comments that this problem may only occur with parallel nested loops joins.&amp;#160; The script below reproduces the problem with parallel hash joins:&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:800px;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;WITH&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;    N1 (n) &lt;span style="color:#0000ff;"&gt;AS&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;/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;-- 10 rows&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;UNION&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;ALL&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; 1&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;/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;    N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R &lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt; L.n % 1 = R.n % 1), &lt;span style="color:#008000;"&gt;-- 100 rows&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;    N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N2 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R &lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt; L.n % 1 = R.n % 1), &lt;span style="color:#008000;"&gt;-- 10,000 rows&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;    N4 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N3 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R &lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt; L.n % 1 = R.n % 1), &lt;span style="color:#008000;"&gt;-- 100,000,000 rows&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;    N5 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color:#0000ff;"&gt;SELECT&lt;/span&gt; L.n &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; N4 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; L &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; N1 &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; R &lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt; L.n % 1 = R.n % 1), &lt;span style="color:#008000;"&gt;-- 1,000,000,000 rows&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;    N6 &lt;span style="color:#0000ff;"&gt;AS&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;/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;TOP&lt;/span&gt; (1000000) n&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; N5&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;/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; N6&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, MAXDOP 4, QUERYTRACEON 8649);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;The full execution plan is rather large, but the key part is shown below (click to enlarge):&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_34C9C4C4.png" target="_blank"&gt;&lt;img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px;" title="parallel hash join plan" border="0" alt="parallel hash join plan" src="http://sqlblog.com/blogs/paul_white/image_thumb_3FA6A60C.png" width="660" height="274" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;Yes, that is &lt;strong&gt;700 million rows&lt;/strong&gt; entering the Gather Streams exchange (click to enlarge):&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_1576F824.png" target="_blank"&gt;&lt;img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px;" title="parallel hash row counts" border="0" alt="parallel hash row counts" src="http://sqlblog.com/blogs/paul_white/image_thumb_52B7ACEB.png" width="644" height="229" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The bug does not occur in every query plan with Top and parallelism, but the choice of nested loops join is not the cause.&lt;/p&gt;

&lt;h3 align="left"&gt;Final Thoughts&lt;/h3&gt;

&lt;p align="left"&gt;The potential for poor performance and excessive processor usage here is obvious; and the chance of hitting the race condition gets &lt;b&gt;worse&lt;/b&gt; at higher DOP.&amp;#160; With eight threads per parallel zone (DOP 8), I hit this issue almost every time on SQL Server 2008, 2008 R2, and 2012.&amp;#160; Because this behaviour does not occur on SQL Server 2005, but does on 2008 and later, I have filed this as a bug on Connect:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="https://connect.microsoft.com/SQLServer/feedback/details/740234/poor-performance-with-parallelism-and-top"&gt;https://connect.microsoft.com/SQLServer/feedback/details/740234/poor-performance-with-parallelism-and-top&lt;/a&gt;&lt;/p&gt;

&lt;h4 align="left"&gt;Further Reading:&lt;/h4&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/archive/2010/08/05/iterators-query-plans-and-why-they-run-backwards.aspx"&gt;http://sqlblog.com/blogs/paul_white/archive/2010/08/05/iterators-query-plans-and-why-they-run-backwards.aspx&lt;/a&gt; 

  &lt;br /&gt;&lt;a title="http://bradsruminations.blogspot.co.nz/2010/11/second-in-life-of-query-operator.html" href="http://bradsruminations.blogspot.co.nz/2010/11/second-in-life-of-query-operator.html"&gt;http://bradsruminations.blogspot.co.nz/2010/11/second-in-life-of-query-operator.html&lt;/a&gt; 

  &lt;br /&gt;&lt;a title="http://www.simple-talk.com/sql/learn-sql-server/understanding-and-using-parallelism-in-sql-server/" 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;br /&gt;&lt;a title="http://blogs.msdn.com/b/craigfr/archive/2006/10/25/the-parallelism-operator-aka-exchange.aspx" href="http://blogs.msdn.com/b/craigfr/archive/2006/10/25/the-parallelism-operator-aka-exchange.aspx"&gt;http://blogs.msdn.com/b/craigfr/archive/2006/10/25/the-parallelism-operator-aka-exchange.aspx&lt;/a&gt; 

  &lt;br /&gt;&lt;a title="http://sqlblog.com/blogs/paul_white/archive/2010/08/18/inside-the-optimiser-row-goals-in-depth.aspx" href="http://sqlblog.com/blogs/paul_white/archive/2010/08/18/inside-the-optimiser-row-goals-in-depth.aspx"&gt;http://sqlblog.com/blogs/paul_white/archive/2010/08/18/inside-the-optimiser-row-goals-in-depth.aspx&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;© 2012 Paul White 
  &lt;br /&gt;Twitter: &lt;a href="http://twitter.com/SQL_Kiwi"&gt;@SQL_Kiwi&lt;/a&gt; (with an underscore) 

  &lt;br /&gt;Email: &lt;a href="mailto:SQLkiwi@gmal.com"&gt;SQLkiwi@gmal.com&lt;/a&gt; (no underscore)&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=43147" 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/Performance/default.aspx">Performance</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/Top/default.aspx">Top</category></item><item><title>Query Optimizer Deep Dive - Part 4</title><link>http://sqlblog.com/blogs/paul_white/archive/2012/05/01/query-optimizer-deep-dive-part-4.aspx</link><pubDate>Mon, 30 Apr 2012 13:36:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:43093</guid><dc:creator>Paul White</dc:creator><slash:comments>7</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/43093.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=43093</wfw:commentRss><description>&lt;p align="left"&gt;This is the final part in a series of posts based on the content of the &lt;i&gt;Query Optimizer Deep Dive&lt;/i&gt; presentations I have given over the last month or so at the &lt;a target="_blank" href="http://www.aucklandsql.com"&gt;Auckland SQL Users’ Group&lt;/a&gt; and the SQL Saturday events in &lt;a target="_blank" href="http://www.sqlsaturday.com/viewsession.aspx?sat=136&amp;amp;sessionid=8411"&gt;Wellington, New Zealand&lt;/a&gt; and &lt;a target="_blank" href="http://www.sqlsaturday.com/viewsession.aspx?sat=139&amp;amp;sessionid=8412"&gt;Adelaide, Australia&lt;/a&gt;.&lt;/p&gt;  &lt;p align="left"&gt;Links to other parts of this series: &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx"&gt;Part 1&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-2.aspx"&gt;Part 2&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/29/query-optimizer-deep-dive-part-3.aspx"&gt;Part 3&lt;/a&gt;&lt;/p&gt;  &lt;h3 align="left"&gt;Beating the Optimizer&lt;/h3&gt;  &lt;p align="left"&gt;Our test query produces an optimized physical execution plan that is quite different from the logical form of the query.&amp;nbsp; The estimated cost of the execution plan shown below is &lt;b&gt;0.0295&lt;/b&gt; units.&lt;/p&gt;  &lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_7B3F183B.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="Optimized Query Plan" border="0" alt="Optimized Query Plan" width="640" height="158" src="http://sqlblog.com/blogs/paul_white/image_thumb_3EC6A391.png"&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p align="left"&gt;Since we know the database schema very well, we might wonder why the optimizer did not choose to use the unique nonclustered index on names in the product table to filter rows based on the LIKE predicate.&amp;nbsp; We could use an index hint to force the name index to be used:&lt;/p&gt;  &lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_69525EA1.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="Forced Index Hint" border="0" alt="Forced Index Hint" width="640" height="159" src="http://sqlblog.com/blogs/paul_white/image_thumb_0A82547E.png"&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p align="left"&gt;That’s great, and no doubt the index seek is cheaper than the scan we had previously, but the optimizer has still chosen to use a merge join, and that means having both inputs sorted on Product ID.&amp;nbsp; The result of the index seek is ordered by name (the index key) rather than Product ID, so a sort is required.&amp;nbsp; It looks like the new sort adds a little more cost than the seek saves over the scan, because the estimated cost of the query plan with the index hint is &lt;b&gt;0.0316&lt;/b&gt; units.&lt;/p&gt;  &lt;p align="left"&gt;Naturally, these numbers are rather small since AdventureWorks is not a large database, but these differences can be important in real systems.&amp;nbsp; Anyway, let’s persist with the index seek idea; why is the optimizer so keen on a merge join, even though it involves an extra sort, and we don’t have an ORDER BY Product ID clause on our query?&amp;nbsp; Without a top-level ORDER BY, we are giving the optimizer the freedom to return results in any order that is convenient – perhaps we can do better by forcing the index seek and a hash join instead of a merge join?&lt;/p&gt;  &lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_26CF969E.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="Forced Hash Join" border="0" alt="Forced Hash Join" width="640" height="159" src="http://sqlblog.com/blogs/paul_white/image_thumb_1FCFE419.png"&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p align="left"&gt;Well the sort has gone, so the plan looks visually a little simpler, but the estimated cost has increased again, to &lt;b&gt;0.0348&lt;/b&gt; units.&amp;nbsp; Hash join has quite a high start-up cost (and it requires a memory workspace grant).&amp;nbsp; We could try other things, but it certainly seems that the optimizer had it right to begin with in this case.&lt;/p&gt;  &lt;p align="left"&gt;The manual exploration above shows that the optimizer does generally find a good plan quickly (and sometimes it may even find the best possible plan). The terms ‘good’ and ‘best’ here are measured in terms of the optimizer’s own cost model. Whether one particular plan shape &lt;i&gt;actually&lt;/i&gt; executes faster on a given system is another question. I might find, for example, that the first merge join plan runs fastest for me, whereas you might find the seek and sort runs fastest for you. We might both find that which is faster depends on whether the indexes and data needed are in memory or need to be retrieved from persistent storage. All these things aside, the important thing is that we are both likely to find that the optimizer’s plans are pretty good most of the time.&lt;/p&gt;  &lt;h3&gt;Models and Limitations&lt;/h3&gt;  &lt;p align="left"&gt;There are three principal models used by the optimizer to provide a basis for its reasoning: cardinality estimation, logical relational equivalences, and physical operator costing.&amp;nbsp; Good cardinality estimation (row count expectations at each node of the logical tree) is vital; if these numbers are wrong, all later decisions are suspect.&amp;nbsp; Fortunately, it is relatively easy to check cardinality estimation by comparing actual and estimated row counts in query plans.&amp;nbsp; There are some subtleties to be aware of – for example when interpreting the inner side of a nested loops join, at least in SSMS.&amp;nbsp; If you use the free &lt;a target="_blank" href="http://www.sqlsentry.net/plan-explorer/sql-server-query-view.asp"&gt;SQL Sentry Plan Explorer&lt;/a&gt; tool, many of these common misinterpretations are handled for you.&lt;/p&gt;  &lt;p align="left"&gt;The model used in cardinality estimation is complex, and contains all sorts of hairy-looking formulas and calculations.&amp;nbsp; Nevertheless, it is still a model, and as such will deviate from reality at least to some extent.&amp;nbsp; I’ll have more to say later on about what we can do to help ensure good cardinality estimates, but for now we will just note that the model has its roots in relational theory and statistical analysis.&lt;/p&gt;  &lt;p align="left"&gt;Relational equivalences (such as A inner join B = B inner join A) are the basis for exploration rules in the cost-based optimizer.&amp;nbsp; Not all possible relational transformations are included in the product (remember the goal of the optimizer is to find a good plan quickly, not to perform an exhaustive search of all possible plans).&amp;nbsp; As a consequence, the SQL syntax you use will often affect the plan produced by the optimizer, even where different SQL syntaxes express the same logical requirement.&amp;nbsp; Also, the skilled query tuner will often be able to do better than the optimizer, given enough time and perhaps a better insight to the data.&amp;nbsp; The downside of such manual tuning is that it will usually require manual intervention again in the future as data volumes and distributions change.&lt;/p&gt;  &lt;p align="left"&gt;Physical operator costing is very much the simplest of the three models, using formulas that have been shown to produce good physical plan selections for a wide range of queries on a wide range of hardware.&amp;nbsp; The numbers used probably do not closely model your hardware or mine, but luckily that turns out not to be too important in most cases.&amp;nbsp; No doubt the model will have to be updated over time as new hardware trends continue to emerge, and there is some evidence in SQL Server 2012 show plan output that things are heading in that direction.&lt;/p&gt;  &lt;h4 align="left"&gt;Assumptions&lt;/h4&gt;  &lt;p align="left"&gt;All models make simplifying assumptions, and the cardinality estimation and costing models are no different.&amp;nbsp; Some things are just too hard to model, and other things just haven’t been incorporated into the model yet.&amp;nbsp; Still other things could be modelled, but turn out not to add much value in practice and cost too much in terms of complexity or resource consumption.&amp;nbsp; Some of the major simplifying assumptions that can affect real plan quality are:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div align="left"&gt;All queries start executing with a cold cache        &lt;br&gt;&lt;i&gt;This isn’t as crazy as it sounds.&amp;nbsp; Fetching data from disk tends to dominate the overall cost of a query, modelling the amount of data that can be expected to be in cache already is hard, and this assumption does at least affect everything equally.&amp;nbsp; The optimizer does contain some logic to account for pages that might be in cache after the first access.&lt;/i&gt;&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="left"&gt;Statistical information is independent        &lt;br&gt;&lt;i&gt;Correlations do frequently exist between columns in real databases, so this assumption can be problematic.&amp;nbsp; Multi-column statistics, filtered indexes, and indexed views can sometimes help with this.&lt;/i&gt;&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="left"&gt;Distribution is uniform        &lt;br&gt;&lt;i&gt;This is assumed where the system has no information to the contrary.&amp;nbsp; One example: costing assumes that seeks (lookups) into an index are randomly distributed throughout the full range of the index.&lt;/i&gt;&lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;Helping the Optimizer&lt;/h3&gt;  &lt;p&gt;There are three approaches to working with the optimizer:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Ignore it.&amp;nbsp; It works well out of the box with default settings and automatic statistics. &lt;/li&gt;    &lt;li&gt;Override it using syntax tricks, hints, and plan guides. &lt;/li&gt;    &lt;li&gt;Provide it with the best information you can, and override it for just a small number of problematic queries. &lt;/li&gt; &lt;/ol&gt;  &lt;p align="left"&gt;All three are valid approaches in different circumstances, though the third option is probably the one to recommend as the best starting point.&amp;nbsp; There is much more to helping the optimizer than just making sure statistics are up-to-date, however:&lt;/p&gt;  &lt;h4 align="left"&gt;Use a relational design&lt;/h4&gt;  &lt;p align="left"&gt;This is probably the biggest single item.&amp;nbsp; The various models all assume that your database has a reasonably relational design, not necessarily 3NF or higher, but the closer your structures are to relational principles, the better.&amp;nbsp; Cardinality estimation works best with simpler relational operations like joins, projects, selects, unions and group by.&amp;nbsp; Avoid complex expressions and non-relational query features that force cardinality estimation to guess.&amp;nbsp; Also remember that the cost-based optimizer’s exploration rules are based on relational equivalences, so having a relational design and writing relational queries gives you the best chance of leveraging the hard work that has gone into those rules.&amp;nbsp; Some features added in 2005 (e.g. ranking functions) operate on sequences rather than multi-sets (hence the Sequence Project physical operator); the original rules all work with multi-sets, and the seams between the two approaches often show though.&lt;/p&gt;  &lt;h4 align="left"&gt;Use constraints&lt;/h4&gt;  &lt;p align="left"&gt;Use check constraints, foreign keys, unique constraints to provide the optimizer with information about your data.&amp;nbsp; Simplification and exploration rules match patterns in the logical tree, and may also require certain properties to be set on the matched nodes.&amp;nbsp; Constraints and keys provide some of the most powerful and fundamental logical properties – omitting these can prevent many of the optimizer’s rules from successfully transforming to an efficient physical plan.&amp;nbsp; If an integer column should only contain certain values, add a check constraint for it.&amp;nbsp; If a foreign key relationship exists, enforce it.&amp;nbsp; If a key exists, declare it.&amp;nbsp; It is not always possible to predict the benefits of providing this type of optimizer information, but my own experience is that it is much greater than most people would ever expect.&lt;/p&gt;  &lt;h4 align="left"&gt;Statistics, indexes and computed columns&lt;/h4&gt;  &lt;p align="left"&gt;Provide more than just default-sampled automatically-created statistics where appropriate (for example where distribution is unusual or correlations exist).&amp;nbsp; Create indexes that will provide potentially useful access paths for a wide range of queries.&amp;nbsp; If you have expressions on columns in your WHERE clause, consider adding a computed column that matches the expression exactly.&amp;nbsp; The computed column does not have to be persisted or indexed to be useful; the system can auto-create statistics on the computed column, avoiding cardinality guessing.&lt;/p&gt;  &lt;h4 align="left"&gt;Deep Trees&lt;/h4&gt;  &lt;p align="left"&gt;Large, complex queries produce large, complex logical trees.&amp;nbsp; Any errors tend to multiply (perhaps exponentially) as the size of the tree increases, the search space of possible plans expands greatly, and things just become much more difficult in general.&amp;nbsp; Breaking a complex query into smaller, simpler, more relational, steps will generally get greater value out of the optimizer.&amp;nbsp; A side benefit is that small intermediate results stored in a temporary table can have statistics created, which will also help in many cases.&amp;nbsp; There will always be cases where a large complex query is required for ultimate performance, but very often the performance difference is relatively small and may not be worth the future maintenance costs as hand-tuned monolithic queries tend to be fragile.&lt;/p&gt;  &lt;h4 align="left"&gt;Opaque operators and new features&lt;/h4&gt;  &lt;p align="left"&gt;User-defined functions (other than the in-line variety) may seem convenient, but they are almost completely opaque to the optimizer – it has to guess at the cardinality and distribution of rows produced.&amp;nbsp; Newer features also tend to have much shallower support in the engine than longer-established features that have been around for multiple versions of the product.&amp;nbsp; By all means use all the shiny new features, just be aware that combining them may produce plans of variable quality.&lt;/p&gt;  &lt;h3 align="left"&gt;Trace Flags&lt;/h3&gt;  &lt;p align="left"&gt;To summarize the flags used in this series (all assume 3604 is also active):&lt;/p&gt;  &lt;p align="left"&gt;7352 : Final query tree    &lt;br&gt;8605 : Converted tree     &lt;br&gt;8606 : Input, simplified, join-collapsed, and normalized trees     &lt;br&gt;8607 : Output tree     &lt;br&gt;8608 : Initial memo     &lt;br&gt;8615 : Final memo     &lt;br&gt;8675 : Optimization stages and times&lt;/p&gt;  &lt;p align="left"&gt;The above were used in the presentation because they all work from 2005 to 2012.&amp;nbsp; There are a large number of other optimizer-related flags (some of which only work on 2012).&amp;nbsp; Some are listed below for people that like this sort of thing:&lt;/p&gt;  &lt;p align="left"&gt;2373 : Memory before and after deriving properties and rules (verbose)    &lt;br&gt;7357 : Unique hash optimization used     &lt;br&gt;8609 : Task and operation type counts     &lt;br&gt;8619 : Apply rule with description     &lt;br&gt;8620 : Add memo arguments to 8619     &lt;br&gt;8621 : Rule with resulting tree&lt;/p&gt;  &lt;p align="left"&gt;As usual, these are undocumented, and unsupported (including by me!) and purely for educational purposes.&amp;nbsp; Use with care at your own risk.&lt;/p&gt;  &lt;h3&gt;Final Thoughts&lt;/h3&gt;  &lt;p align="left"&gt;I hope you have gained some insight and intuition for the workings of the query optimizer: logical trees, simplification, cardinality estimation, logical exploration and physical implementation.&amp;nbsp; The geeky internals stuff is fun, of course, but I rather hope people came away from these sessions with a better understanding of how query text is transformed to an executable plan, and how relational designs and simpler queries can help the optimizer work well for you.&amp;nbsp; Taking advantage of the optimizer frees up time for more productive things – new projects, tuning the odd interesting query, whatever it is you would rather be doing than fighting the same query-tuning battles over and over again.&lt;/p&gt;  &lt;p align="left"&gt;There are some great resources out there to learn more about SQL Server. Make full use of them to continue to improve your technical skills and just as importantly, experiment with things to build your experience level. Take a deeper look at query plans and the properties they contain; there is a wealth of information there that sometimes requires a bit of thinking and research to understand, but the insights can be invaluable.&lt;/p&gt;  &lt;p align="left"&gt;The original slides and demo code is again attached as a zip file below.&amp;nbsp; Thanks for reading, all comments and feedback are very welcome.&lt;/p&gt;  &lt;p align="left"&gt;Finally, I would like to thank Adam Machanic (&lt;a target="_blank" href="http://sqlblog.com/blogs/adam_machanic"&gt;blog&lt;/a&gt; | &lt;a target="_blank" href="https://twitter.com/#!/AdamMachanic"&gt;twitter&lt;/a&gt;) for introducing me to the QUERYTRACEON syntax all that time ago, and Fabiano Amorim (&lt;a target="_blank" href="http://blogfabiano.com/"&gt;blog&lt;/a&gt; | &lt;a target="_blank" href="https://twitter.com/#!/mcflyamorim"&gt;twitter&lt;/a&gt;) for his email last year that kick-started the process of developing this presentation and blog series.&lt;/p&gt;  &lt;p align="left"&gt;Links to other parts of this series: &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx"&gt;Part 1&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-2.aspx"&gt;Part 2&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/29/query-optimizer-deep-dive-part-3.aspx"&gt;Part 3&lt;/a&gt;&lt;/p&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;img src="http://sqlblog.com/aggbug.aspx?PostID=43093" width="1" height="1"&gt;</description><enclosure url="http://sqlblog.com/blogs/paul_white/attachment/43093.ashx" length="1955066" type="application/x-zip-compressed" /><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Costing/default.aspx">Costing</category><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+Optimizer/default.aspx">Query Optimizer</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/Undocumented/default.aspx">Undocumented</category></item><item><title>Query Optimizer Deep Dive – Part 3</title><link>http://sqlblog.com/blogs/paul_white/archive/2012/04/29/query-optimizer-deep-dive-part-3.aspx</link><pubDate>Sun, 29 Apr 2012 07:30:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:43069</guid><dc:creator>Paul White</dc:creator><slash:comments>5</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/43069.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=43069</wfw:commentRss><description>&lt;p&gt;This is the third part in a series of posts based on the content of the &lt;i&gt;Query Optimizer Deep Dive&lt;/i&gt; presentations I have given over the last month or so at the &lt;a target="_blank" href="http://www.aucklandsql.com"&gt;Auckland SQL Users’ Group&lt;/a&gt; and the SQL Saturday events in &lt;a target="_blank" href="http://www.sqlsaturday.com/viewsession.aspx?sat=136&amp;amp;sessionid=8411"&gt;Wellington, New Zealand&lt;/a&gt; and &lt;a target="_blank" href="http://www.sqlsaturday.com/viewsession.aspx?sat=139&amp;amp;sessionid=8412"&gt;Adelaide, Australia&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Links to other parts of this series: &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx"&gt;Part 1&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-2.aspx"&gt;Part 2&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/05/01/query-optimizer-deep-dive-part-4.aspx"&gt;Part 4&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;Storage of Alternative Plans&lt;/h3&gt;  &lt;p align="left"&gt;We saw in part 2 how optimizer rules are used to explore logical alternatives for parts of the query tree, and how implementation rules are used to find physical operations to perform each logical steps.&amp;nbsp; To keep track of all these options, the cost-based part of the SQL Server query optimizer uses a structure called the Memo.&amp;nbsp; This structure is part of the &lt;a target="_blank" href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.98.9460"&gt;Cascades&lt;/a&gt; general optimization framework developed by Goetz Graefe.&amp;nbsp; The Memo provides an efficient way to store many plan alternatives via the use of groups.&lt;/p&gt;  &lt;p align="left"&gt;Each group in the Memo initially contains just one entry – a node from the input logical tree.&amp;nbsp; As exploration and implementation phases progress, new groups may be added, and new physical and logical alternatives may be added to existing groups (all alternatives in a group share the same logical properties, but will often have quite different physical properties).&amp;nbsp; Groups are also shared between plan alternatives where possible, allowing Cascades to search more plans in the same time and space compared with other optimization frameworks.&lt;/p&gt;  &lt;p align="left"&gt;Continuing with our example query, the input tree to cost-based optimization is first copied into the Memo structure.&amp;nbsp; We can see this structure using trace flag 8608:&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:400px;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;USE&lt;/span&gt; AdventureWorks2008R2;&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; NOCOUNT &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:#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;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; TRACEON(3604);&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:#008000;"&gt;-- Initial memo contents&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;    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;    Total = &lt;span style="color:#0000ff;"&gt;SUM&lt;/span&gt;(inv.Quantity)&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.ProductInventory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; inv &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;    inv.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;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;'[A-G]%'&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;GROUP&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; (QUERYTRACEON 8608);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;As a reminder, this is the logical input tree to cost-based optimization shown in part 2:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_2FEBE882.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="Cost-Based Optimization Input Tree" border="0" alt="Cost-Based Optimization Input Tree" width="660" height="395" src="http://sqlblog.com/blogs/paul_white/image_thumb_73DFA6CC.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is copied to the Memo, one group per logical node, illustrated below:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_102CE8ED.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="Optimizer Initial Memo Contents" border="0" alt="Optimizer Initial Memo Contents" width="660" height="280" src="http://sqlblog.com/blogs/paul_white/image_thumb_3254CEA6.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The group numbers and linked structure shown are obtained directly from the trace flag output:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_5FA63EA9.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="Trace Flag 8608 Output" border="0" alt="Trace Flag 8608 Output" width="321" height="572" src="http://sqlblog.com/blogs/paul_white/image_thumb_3244A8D9.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;Each group has an entry number (all zero above since there is only one logical entry per group at this stage), with a logical operation (e.g. LogOp_Join), and the group numbers of any child groups (e.g. the logical join in group 13 references its two inputs, groups 8 and 9, and a logical comparison defined by the sub-tree starting at group 12).&lt;/p&gt;

&lt;h3&gt;Optimization Phases&lt;/h3&gt;

&lt;p align="left"&gt;One the initial Memo has been populated from the input tree, the optimizer runs up to four phases of search.&amp;nbsp; There is a documented DMV, &lt;a target="_blank" href="http://technet.microsoft.com/en-us/library/ms175002.aspx"&gt;sys.dm_exec_query_optimizer_info&lt;/a&gt;, that contains a number of counters specific to query optimization.&amp;nbsp; Four of these counters are ‘trivial plan’, search 0’, ‘search 1’, and ‘search 2’.&amp;nbsp; The value for each counter keeps track of the number of times a search phase was entered.&amp;nbsp; By recording the values before and after a specific optimization, we can see which phases were entered.&amp;nbsp; The current counter values on one of my test SQL Server instances is shown below, as an example:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_1840C2EA.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="sys.dm_exec_query_optimizer_info" border="0" alt="sys.dm_exec_query_optimizer_info" width="265" height="129" src="http://sqlblog.com/blogs/paul_white/image_thumb_497F877D.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;We look at each of the four phases in a bit more detail next:&lt;/p&gt;

&lt;h4&gt;Search 0 – Transaction Processing&lt;/h4&gt;

&lt;p align="left"&gt;This phase is primarily concerned with finding good plans for OLTP-type queries, which usually join a number of tables using a navigational strategy (looking up a relatively small number of rows using an index).&amp;nbsp; This phase primarily considers nested-loops joins, though hash match may be used when a loops join implementation is not possible.&amp;nbsp; Many of the more advanced (and costly to explore) optimizer rules are not enabled during this search phase – for example search 0 never considers indexed view matching or parallel plans.&lt;/p&gt;

&lt;h4&gt;Search 1 – Quick Plan (also known as Complex Query I)&lt;/h4&gt;

&lt;p&gt;This search phase can use most or all of the available rules, can perform limited join reordering, and may be run a second time (to consider parallel plans only) if the first run produces a plan with a high enough cost.&amp;nbsp; Most queries find a final plan during one of the Search 1 runs.&lt;/p&gt;

&lt;h4&gt;Search 2 – Full Optimization&lt;/h4&gt;

&lt;p align="left"&gt;This phase uses the most comprehensive search configuration, and may result in significant compilation times in some cases.&amp;nbsp; The search is either for a serial or parallel plan, depending on which type was cheaper after search 1.&lt;/p&gt;

&lt;p align="left"&gt;The scripts accompanying this series show another way to see which phases were run, using trace flag 8675.&amp;nbsp; The output provides some extra insight into how things work (for example it shows the number of optimization moves (tasks) made in each stage).&amp;nbsp; The only documented and supported way to see the search phases is via the DMV, however.&lt;/p&gt;

&lt;h4 align="left"&gt;Entry and Termination Conditions&lt;/h4&gt;

&lt;p align="left"&gt;Each phase has entry conditions, a set of enabled rules, and termination conditions.&amp;nbsp; Entry conditions mean that a phase may be skipped altogether; for example, search 0 requires at least three joined tables in the input tree.&amp;nbsp; Termination conditions help to ensure the optimizer does not spend more time optimizing than it saves – if the current lowest plan cost drops below a configured value, the search will terminate early with a ‘Good Enough Plan Found’ result.&lt;/p&gt;

&lt;p align="left"&gt;The optimizer also sets a budget at the start of a phase for the number of optimization ‘moves’ it considers sufficient to find a pretty good plan (remember the optimizer’s goal is to find a good enough plan quickly).&amp;nbsp; If the process of exploring and implementing alternatives exceeds this ‘budget’ during a phase, the phase terminates with a ‘Time Out’ message.&amp;nbsp; Early termination (for whatever reason) is part of the optimizer’s design, completely normal, and not generally a cause for concern.&lt;/p&gt;

&lt;p align="left"&gt;From time to time, we might wish that the optimizer had different goals – perhaps that we could ask it to continue searching for longer – but this is not how it works.&amp;nbsp; It is all too easy to over-spend on optimization (finding transformations is not hard – finding ones that are robustly useful in a wide range of circumstances is), and full cost-based exploration is a memory and processor-intensive operation.&amp;nbsp; The optimizer contains many heuristics, checks and limits to avoid exploring unpromising alternatives, to prune the search space as it goes, and ultimately produce a pretty good plan pretty quickly, most of the time, on most hardware, in most databases.&lt;/p&gt;

&lt;h3&gt;Costing&lt;/h3&gt;

&lt;p align="left"&gt;First a quick summary: The optimizer runs up to four phases, each of which performs exploration using rules (finding new logical alternatives to some part of the tree), then a round of implementation rules (finding physical implementations for a logical part of the tree).&amp;nbsp; New logical or physical alternatives are added to the Memo structure – either as an alternative within an existing group, or as a completely new group.&amp;nbsp; Note that the Memo allows the optimizer to consider very many alternatives at once in a compact and quick-to-search structure; the optimizer does &lt;b&gt;not&lt;/b&gt; just work on one overall plan, performing incremental improvements (this is a common misconception).&lt;/p&gt;

&lt;p align="left"&gt;Having found all these alternatives, the optimizer needs a way to choose between them.&amp;nbsp; Costing runs after each round of implementation rules, producing a cost value for each physical alternative in each group in the memo (only physical operations can be costed, naturally).&amp;nbsp; Costing considers factors like cardinality, average row size, expected sequential and random I/O operations, processor time, buffer pool memory requirements, and the effect of parallel execution.&lt;/p&gt;

&lt;p align="left"&gt;Like many areas of optimization, the costing calculations are based on a complex mathematical model.&amp;nbsp; This model generally provides a good basis on which to compare alternatives internally, regardless of the workload or particular hardware configuration.&amp;nbsp; The costing numbers do not mean very much by themselves, so it is generally unwise to compare them between statements or across different queries.&amp;nbsp; The numbers are perhaps best thought of as a unit-less quantity, useful only when comparing alternative plans for the same statement.&lt;/p&gt;

&lt;p align="left"&gt;The model is just that of course: a model, albeit one that happens to produce very good results for most people most of the time.&amp;nbsp; The slide deck that accompanies this series contains details of some of the simplifying assumptions made in the model.&lt;/p&gt;

&lt;h3&gt;Final Memo Contents&lt;/h3&gt;

&lt;p align="left"&gt;At the end of each search phase, the Memo may have expanded to include new logical and physical alternatives for each group, perhaps some completely new groups.&amp;nbsp; We can see the contents after each phase using trace flag 8615:&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:#008000;"&gt;-- Final memo&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;    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;    Total = &lt;span style="color:#0000ff;"&gt;SUM&lt;/span&gt;(inv.Quantity)&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.ProductInventory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; inv &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;    inv.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;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;'[A-G]%'&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;GROUP&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 8615);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;The final Memo (after each phase, remember) can be quite large, despite the techniques employed to constrain the search space.&amp;nbsp; Our simple test query only requires a single run through search 1, and terminates early after 509 moves (tasks) with a ‘Good Enough Plan Found’ message.&amp;nbsp; Despite that, the Memo contains 38 groups (up from 18) with many groups containing several alternatives.&amp;nbsp; The extract below highlights just the groups that relate directly to the join implementation:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_19E168F1.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="Final Memo Trace Flag 8615 Contents" border="0" alt="Final Memo Trace Flag 8615 Contents" width="640" height="559" src="http://sqlblog.com/blogs/paul_white/image_thumb_5C908E5C.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;Looking at the green shaded areas, group 13 item 0 was the original (joining groups 8 and 9 and applying the condition described by the sub-tree starting with group 12).&amp;nbsp; It has gained an logical alternative (via the Join Commute rule, reversing the join input groups), and two surviving physical implementations: a one-to-many merge join as item 7, with group 8 option 2 as the first input, and group 9 option 2 as the second; and a physical inner Apply driven by group 8 option 6, on group 30 option 2.&amp;nbsp; Group 20 is completely new – another one-to-many merge join option, but this time with inputs from groups 19.4 and 8.2 with 12 familiar as the join condition.&amp;nbsp; Again, the scripts accompanying this series can be used to explore the details further, if you wish.&lt;/p&gt;

&lt;p align="left"&gt;The final selected plan consists of physical implementations chosen by starting with the lowest-cost physical option in the root group (18, shaded pink above).&amp;nbsp; The sub-tree from that point has a total estimated cost of 0.0295655 (this is the same cost shown in the final graphical execution plan).&amp;nbsp; The plan is produced by following the links in the Memo, from group 18 option 4, to group 20 option 6, and so on to the leaves:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_18207750.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="Good Enough Plan Found" border="0" alt="Good Enough Plan Found" width="289" height="209" src="http://sqlblog.com/blogs/paul_white/image_thumb_5B3BCFB0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_778911D0.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="Optimized Query Plan" border="0" alt="Optimized Query Plan" width="499" height="205" src="http://sqlblog.com/blogs/paul_white/image_thumb_657CCE43.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;End of Part 3&lt;/h4&gt;

&lt;p align="left"&gt;The original slides and demo code are attached below as a zip file.&lt;/p&gt;

&lt;p align="left"&gt;Links to other parts of this series: &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx"&gt;Part 1&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-2.aspx"&gt;Part 2&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/05/01/query-optimizer-deep-dive-part-4.aspx"&gt;Part 4&lt;/a&gt;&lt;/p&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;img src="http://sqlblog.com/aggbug.aspx?PostID=43069" width="1" height="1"&gt;</description><enclosure url="http://sqlblog.com/blogs/paul_white/attachment/43069.ashx" length="1955066" type="application/x-zip-compressed" /><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Costing/default.aspx">Costing</category><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+Optimizer/default.aspx">Query Optimizer</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/Undocumented/default.aspx">Undocumented</category></item><item><title>Query Optimizer Deep Dive – Part 2</title><link>http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-2.aspx</link><pubDate>Sat, 28 Apr 2012 04:56:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:43049</guid><dc:creator>Paul White</dc:creator><slash:comments>6</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/43049.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=43049</wfw:commentRss><description>&lt;p align="left"&gt;This is the second part in a series of posts based on the content of the &lt;i&gt;Query Optimizer Deep Dive&lt;/i&gt; presentations I have given over the last month or so at the &lt;a target="_blank" href="http://www.aucklandsql.com"&gt;Auckland SQL Users’ Group&lt;/a&gt; and the SQL Saturday events in &lt;a target="_blank" href="http://www.sqlsaturday.com/viewsession.aspx?sat=136&amp;amp;sessionid=8411"&gt;Wellington, New Zealand&lt;/a&gt; and &lt;a target="_blank" href="http://www.sqlsaturday.com/viewsession.aspx?sat=139&amp;amp;sessionid=8412"&gt;Adelaide, Australia&lt;/a&gt;.&lt;/p&gt;  &lt;p align="left"&gt;Links to other parts of this series: &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx"&gt;Part 1&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/29/query-optimizer-deep-dive-part-3.aspx"&gt;Part 3&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/05/01/query-optimizer-deep-dive-part-4.aspx"&gt;Part 4&lt;/a&gt;&lt;/p&gt;  &lt;h3 align="left"&gt;Cost-Based Optimization Overview&lt;/h3&gt;  &lt;p align="left"&gt;The input to cost-based optimization is a tree of logical operations produced by the previous optimization stages discussed in part one.&amp;nbsp; Cost-based optimization takes this logical tree, explores logical alternatives (different logical tree shapes that produce the same results), generates physical implementations, assigns an estimated cost to each, and finally chooses the cheapest physical option overall.&lt;/p&gt;  &lt;p align="left"&gt;The goal of cost-based optimization is &lt;i&gt;not&lt;/i&gt; to find the best possible physical execution plan by exploring every possible alternative; rather, the goal is to find a good plan quickly.&amp;nbsp; This approach gives us an optimizer that works pretty well for most workloads, most of the time.&amp;nbsp; If you think about it, that’s quite an achievement – after all, my database is likely very different from yours, and we probably run on quite different hardware as well.&amp;nbsp; The point about finding a good plan quickly is also important – generally, we would not want the optimizer to spend hours optimizing a query that runs for only a few seconds.&lt;/p&gt;  &lt;p align="left"&gt;This design has a number of important consequences.&amp;nbsp; First, a skilled query tuner will often be able to come up with a better plan than the optimizer does.&amp;nbsp; In some cases, this will because the human can reason about the query in a way the optimizer cannot.&amp;nbsp; Other times, it is simply a question of time – we might be happy spending half a day finding a great execution plan for a crucial query, whereas the optimizer places strict limits on itself to avoid spending more time on optimization than it saves on a single execution.&lt;/p&gt;  &lt;p align="left"&gt;Perhaps a future version of SQL Server will allow us to configure the optimizer to spend extra time on a particular query – today, we have to use query hints and ‘manual optimizations’ to encourage a particular execution plan shape.&amp;nbsp; The down side to circumventing the optimizer’s natural behaviour in this way is that we then have to take responsibility for ensuring that the execution plan remains good in the future, as data volumes and distributions change.&amp;nbsp; In most systems, it is best to limit the number of manually-tuned queries to a minimum, and thoroughly document any tricks you use to obtain a particular plan.&lt;/p&gt;  &lt;h3 align="left"&gt;Input Tree to Cost-Based Optimization&lt;/h3&gt;  &lt;p align="left"&gt;Back to the task at hand.&amp;nbsp; We have a sample query (reproduced below) that has been through all the previous optimization stages, did not qualify for a trivial plan, and so needs to go through the cost-based optimization process.&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;    Total = &lt;span style="color:#0000ff;"&gt;SUM&lt;/span&gt;(inv.Quantity)&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; &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;    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;    Production.ProductInventory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; inv&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;    inv.ProductID = p.ProductID&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;AND&lt;/span&gt; p.Name &lt;span style="color:#0000ff;"&gt;LIKE&lt;/span&gt; N&lt;span style="color:#006080;"&gt;'[A-G]%'&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;GROUP&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;The tree of logical operations passed to the optimizer looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_4180456C.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="Cost-Based Optimizer Input Tree" border="0" alt="Cost-Based Optimizer Input Tree" width="640" height="375" src="http://sqlblog.com/blogs/paul_white/image_thumb_5D31A4D7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;This looks very similar to the original logical tree seen in part one, though cardinality estimates have been added to each primary node, and the tree layout has been expanded a little into a form that is easier for the system to work with.&amp;nbsp; In addition to simple cardinality estimates, remember that each node also has statistics objects (histograms and frequency information) derived from the familiar statistics associated with the tables in the query.&lt;/p&gt;

&lt;p align="left"&gt;The tree above was built from information obtained using trace flag 3604 and 8606 (see part one for more details).&amp;nbsp; Trace flag 8606 shows the tree at various stages, the diagram above is built from the view after project normalization:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_23323C1E.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="Trace Flag 8606 Output" border="0" alt="Trace Flag 8606 Output" width="655" height="566" src="http://sqlblog.com/blogs/paul_white/image_thumb_154FD056.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 align="left"&gt;Properties&lt;/h3&gt;

&lt;p align="left"&gt;There are also a number of other ‘properties’ associated with each node, that have been added by previous optimization stages.&amp;nbsp; These properties include logical attributes, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;div align="left"&gt;Output columns and expressions&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Uniqueness (key) information&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Type information and nullability&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Functional dependencies&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Domain ranges for each column or expression&lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p align="left"&gt;There are also some physical properties that may be present on each node, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;div align="left"&gt;Sort order at each node&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Partitioning information&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Halloween protection&lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p align="left"&gt;The Halloween protection information is worth explaining in a bit more detail.&amp;nbsp; A query generally executes as a pipeline, with rows being requested one at a time from the top of the tree – so rows are in effect pulled up the tree one at a time.&amp;nbsp; This pipeline arrangement has a number of important advantages, but a problem can arise where the query modifies data: changes made by the query can affect rows being read by the same query – the classic example is a query that adds 10% to the salaries of every employee.&amp;nbsp; When processed as a pipeline, we can get stuck in an infinite loop as rows that have had 10% added re-qualify for reading.&amp;nbsp; This problem happened to be first discovered on 31 October 1976 and became known as the &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Halloween_Problem"&gt;Halloween Problem&lt;/a&gt;.&lt;/p&gt;

&lt;p align="left"&gt;One solution is to separate operations that read data from those that update data by reading all rows into temporary storage before performing any updates.&amp;nbsp; In SQL Server, this simple solution typically manifests as an Eager Table Spool in the query plan – all rows are written eagerly to temporary storage before any updates are performed.&amp;nbsp; This is a bit of a sledgehammer solution though, so the optimizer keeps track of which columns need protection from the Halloween problem (and how much protection they need) at each node by setting properties.&amp;nbsp; Some logical operations (like sorting) naturally mean that all input rows have to be read before the first output row can be produced.&amp;nbsp; These operations provide Halloween protection naturally, so an explicit Eager Table Spool would not be necessary.&amp;nbsp; There are a number of logical operations that can provide some degree of Halloween protection, and properties are used to ensure that just enough protection is provided, at minimum cost.&lt;/p&gt;

&lt;h3 align="left"&gt;Rules&lt;/h3&gt;

&lt;p align="left"&gt;The optimizer contains rules to transform trees.&amp;nbsp; There are four classes of rule:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;div align="left"&gt;Simplification&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Exploration&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Implementation&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Physical property enforcement&lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simplification rules transform some part of the logical tree to a simpler logical form, and are responsible for the simplification transforms we saw in part one.&amp;nbsp; Exploration rules run only during cost-based optimization, generating new logical equivalents for some part of the existing logical tree.&amp;nbsp; Implementation rules produce a physical operation (like a hash or merge join) for a logical operation (a logical join).&amp;nbsp; Property enforcement rules introduce new elements to the logical tree to enforce some desired physical property at that point, for example to enforce a particular sorted order of rows (as might be required by a merge join or stream aggregate).&lt;/p&gt;

&lt;p align="left"&gt;There are 395 rules in SQL Serer 2012, most of which perform quite simple operations; it is the fact that many rules can be run in various orders that accounts for the apparent complexity of optimizer behaviour.&amp;nbsp; The way that simple rules can combine to produce complex behaviours reminds me of &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life"&gt;Conway’s Game of Life&lt;/a&gt; – a cellular automation program with only four rules that nevertheless can produce extraordinarily complex and beautiful patterns as the rules are applied over and over.&lt;/p&gt;

&lt;h4 align="left"&gt;Exploration Rule Examples&lt;/h4&gt;

&lt;p align="left"&gt;One example of a simple exploration rule is SELonJN, a rule that merges a suitable relational SELECT (row filter) into a logical join operation:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_63B43CF3.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="SELonJN Exploration Rule" border="0" alt="SELonJN Exploration Rule" width="480" height="304" src="http://sqlblog.com/blogs/paul_white/image_thumb_0835A4AB.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another common exploration rule that generates a new logical alternative is JoinCommute:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_694F0AFF.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="JoinCommute Exploration Rule" border="0" alt="JoinCommute Exploration Rule" width="640" height="319" src="http://sqlblog.com/blogs/paul_white/image_thumb_71A2BA89.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;As the name suggests, JoinCommute explores a different logical join order, exploiting the fact that A JOIN B is equivalent to B JOIN A for inner joins.&amp;nbsp; Though logically equivalent, the different join orders may have different performance characteristics once the logical alternatives are translated to a physical implementation by a later implementation rule.&amp;nbsp; A third exploration rule that is relevant to our test query is GbAggBeforeJoin, a rule that explores the possibility of pushing an aggregation operation under a join:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_0BD2FDE1.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="GbAggBeforeJoin Transformation" border="0" alt="GbAggBeforeJoin Transformation" width="480" height="486" src="http://sqlblog.com/blogs/paul_white/image_thumb_050FCE91.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4 align="left"&gt;Implementation Rule Examples&lt;/h4&gt;

&lt;p align="left"&gt;As mentioned previously, implementation rules transform part of a logical tree into a physical alternative:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_61932A2B.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="Optimizer Implementation Rules" border="0" alt="Optimizer Implementation Rules" width="640" height="263" src="http://sqlblog.com/blogs/paul_white/image_thumb_42405D8B.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The diagram shows three join implementation rules, JNtoNL (nested loops), JNtoHS (hash join), JNtoSM (sort-merge join); two implementations for Group-By Aggregate, GbAggToStrm (Stream Aggregate) and GbAggToHS (Hash Aggregate); SelectToFilter, GetToScan, and GetIdxToRng.&lt;/p&gt;

&lt;h3&gt;Which Rules Were Used?&lt;/h3&gt;

&lt;p align="left"&gt;If we want to see which rules were used when optimizing a query, one option is to use an undocumented view which shows the number of times a rule has been asked to estimate how valuable it might be in the current context (its ‘promise’ value), the number of times the rule was used to generate an alternative section of the tree (‘built’), and the number of times the output of the rule was successfully incorporated into the search space (‘succeeded’).&amp;nbsp; A sample of the contents of the sys.dm_exec_query_transformation_stats DMV is shown below:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_74940B3D.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="sys.dm_exec_query_transformation_stats" border="0" alt="sys.dm_exec_query_transformation_stats" width="640" height="305" src="http://sqlblog.com/blogs/paul_white/image_thumb_462A5FC3.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;By taking a snapshot view of this information before and after optimizing a particular query, we can see which rules were run.&amp;nbsp; The DMV is instance-wide however, so you would need to run these tests on a system to which you have exclusive access.&amp;nbsp; The scripts that accompany this series of posts contain a complete test rig to show the rules used by each query.&lt;/p&gt;

&lt;h3 align="left"&gt;End of Part 2&lt;/h3&gt;

&lt;p align="left"&gt;&lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/29/query-optimizer-deep-dive-part-3.aspx"&gt;Part three&lt;/a&gt; in this series covers the Memo structure and optimization phases in detail.&amp;nbsp; The original presentation slides and demo code are contained in the zip file shown below.&lt;/p&gt;

&lt;p align="left"&gt;Links to other parts of this series: &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx"&gt;Part 1&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/29/query-optimizer-deep-dive-part-3.aspx"&gt;Part 3&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/05/01/query-optimizer-deep-dive-part-4.aspx"&gt;Part 4&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&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;img src="http://sqlblog.com/aggbug.aspx?PostID=43049" width="1" height="1"&gt;</description><enclosure url="http://sqlblog.com/blogs/paul_white/attachment/43049.ashx" length="1955066" type="application/x-zip-compressed" /><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Costing/default.aspx">Costing</category><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+Optimizer/default.aspx">Query Optimizer</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/Undocumented/default.aspx">Undocumented</category></item><item><title>Query Optimizer Deep Dive - Part 1</title><link>http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx</link><pubDate>Fri, 27 Apr 2012 14:38:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:43025</guid><dc:creator>Paul White</dc:creator><slash:comments>12</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/43025.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=43025</wfw:commentRss><description>&lt;p align="left"&gt;This is the first in a series of posts based on the content of the &lt;i&gt;Query Optimizer Deep Dive&lt;/i&gt; presentations I have given over the last month or so at the &lt;a target="_blank" href="http://www.aucklandsql.com"&gt;Auckland SQL Users’ Group&lt;/a&gt; and the SQL Saturday events in &lt;a target="_blank" href="http://www.sqlsaturday.com/viewsession.aspx?sat=136&amp;amp;sessionid=8411"&gt;Wellington, New Zealand&lt;/a&gt; and &lt;a target="_blank" href="http://www.sqlsaturday.com/viewsession.aspx?sat=139&amp;amp;sessionid=8412"&gt;Adelaide, Australia&lt;/a&gt;.&lt;/p&gt;  &lt;p align="left"&gt;Links to other parts of this series: &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-2.aspx"&gt;Part 2&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/29/query-optimizer-deep-dive-part-3.aspx"&gt;Part 3&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/05/01/query-optimizer-deep-dive-part-4.aspx"&gt;Part 4&lt;/a&gt;&lt;/p&gt;  &lt;h3 align="left"&gt;Introduction&lt;/h3&gt;  &lt;p align="left"&gt;The motivation behind writing these sessions is that I have found that relatively few people have a good intuition for the way the optimizer works, partly because the official documentation is rather sparse, and partly because what information is available is dispersed across many books and blog posts. The content presented here is very much geared to my preferred way of learning – it shows the concepts in what seems to me to be a reasonably logical sequence, and provides tools to enable the interested reader to explore further, if desired.&lt;/p&gt;  &lt;p align="left"&gt;When we write a query, we are &lt;i&gt;not&lt;/i&gt; writing a computer program that can be directly executed.&amp;nbsp; SQL is a declarative language used to logically describe the results we want to see.&amp;nbsp; SQL Server goes through a process of compilation and optimization to create a physically executable implementation of the stated logical requirement.&amp;nbsp; To put it another way, a SQL query is the &lt;i&gt;specification&lt;/i&gt; for an executable program that SQL Server writes for us.&amp;nbsp; The SQL Server component responsible for finding an efficient physical execution plan for a given logical query requirement is the Query Optimizer.&lt;/p&gt;  &lt;h3 align="left"&gt;The SQL Server Core Engine&lt;/h3&gt;  &lt;p align="left"&gt;The diagram below shows conceptually where the Query Optimizer sits in the core SQL Server engine:&lt;/p&gt;  &lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_4FBDD2B1.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="SQL Server Core Engine Diagram" border="0" alt="SQL Server Core Engine Diagram" width="640" height="265" src="http://sqlblog.com/blogs/paul_white/image_thumb_79DD5ACC.png"&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p align="left"&gt;Language Processing is concerned with things like parsing the text of the query to make sure it is syntactically valid, binding table references to real database objects, deriving types for expressions, semantic analysis (for example checking that the query is not trying to execute a table), and binding GROUP BY references to the appropriate logical scope.&amp;nbsp; The Query Optimizer aims to find a good physical execution plan that matches the logical semantic of the query, and the Query Executor is responsible for running that plan to completion.&amp;nbsp; Below that, the Storage Engine provides access to physical storage, together with locking and transaction services.&amp;nbsp; Finally, SQL-OS is the layer that provides threading, memory management, and scheduling services.&lt;/p&gt;  &lt;p align="left"&gt;Using the same colours for the components shown above, a very high-level overview of query processing looks like this:&lt;/p&gt;  &lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_0F77936A.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="Query Processing Overview" border="0" alt="Query Processing Overview" width="640" height="46" src="http://sqlblog.com/blogs/paul_white/image_thumb_7B11CDDE.png"&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p align="left"&gt;This series of blog posts is mostly concerned with the “Optimize” step above.&amp;nbsp; It takes as its input a tree of logical operations produced by the previous stages.&lt;/p&gt;  &lt;h3 align="left"&gt;Optimization Pipeline&lt;/h3&gt;  &lt;p align="left"&gt;The diagram below shows the principal stages within query optimization, starting with the bound tree produced by the parser and algebrizer.&amp;nbsp; The coloured steps are the ones we will explore in depth – the right hand side of the diagram lists a bit more detail that I talk through when presenting this material live:&lt;/p&gt;  &lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_437C0CF0.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="Query Optimization Pipeline" border="0" alt="Query Optimization Pipeline" width="640" height="433" src="http://sqlblog.com/blogs/paul_white/image_thumb_74BAD183.png"&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h3 align="left"&gt;Input Tree&lt;/h3&gt;  &lt;p align="left"&gt;We start by looking at the bound logical tree (input tree).&amp;nbsp; To make the discussion a little less abstract, we will use a test query issued against the AdventureWorks sample database.&amp;nbsp; This simple query shows products and the quantity in stock, limited to products with names that start with a letter between A and G:&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:#008000;"&gt;-- Test query&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;    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;    Total = &lt;span style="color:#0000ff;"&gt;SUM&lt;/span&gt;(inv.Quantity)&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; &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;    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;    Production.ProductInventory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; inv&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;    inv.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;AND&lt;/span&gt; p.Name &lt;span style="color:#0000ff;"&gt;LIKE&lt;/span&gt; N&lt;span style="color:#006080;"&gt;'[A-G]%'&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;GROUP&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:#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;/div&gt;
&lt;/div&gt;

&lt;p&gt;The input tree generated by this query is shown below, slightly simplified:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_5C67B768.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="Optimizer Logical Input Tree" border="0" alt="Optimizer Logical Input Tree" width="243" height="480" src="http://sqlblog.com/blogs/paul_white/image_thumb_193C393B.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The operations in this tree are all purely logical, and have their roots in Relational Theory.&amp;nbsp; The GET operation logically reads an entire table, though you should not yet be thinking in terms of physical operations such as a scan or a seek.&amp;nbsp; The JOIN is a cartesian product, logically joining every row in one table with every row in the other.&amp;nbsp; The SELECT is not a SQL statement, it is a relational operation: filtering rows based on some condition (known as a predicate).&amp;nbsp; Here, we are applying two SELECT operations to filter on matching product IDs and rows where the product name starts with a letter between A and G.&amp;nbsp; The next logical operation is a Group-By Aggregate, an extension to the basic relational algebra that logically groups rows by some common attribute, and optionally computes an aggregate expression for each group.&amp;nbsp; Finally, we have a relational Project operation, which filters columns – here we only need the Name and Total columns, and those are the only two returned in the logical result set.&lt;/p&gt;

&lt;p align="left"&gt;To see this tree directly, we can use undocumented trace flag 8605.&amp;nbsp; This flag also requires the common trace flag 3604 to produce output to the SSMS messages window.&amp;nbsp; We can use another undocumented syntax, the QUERYTRACEON hint, to set the trace flag just for 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:#008000;"&gt;-- Input tree (ISO-92)&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;    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;    Total = &lt;span style="color:#0000ff;"&gt;SUM&lt;/span&gt;(inv.Quantity)&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.ProductInventory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; inv &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;    inv.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;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;'[A-G]%'&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;GROUP&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 8605);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;Now, in the SSMS messages tab, we see a textual representation of the tree of logical operators I drew with coloured boxes before:&lt;/p&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_03FE0DC6.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="Trace Flag 8605 Output" border="0" alt="Trace Flag 8605 Output" width="660" height="626" src="http://sqlblog.com/blogs/paul_white/image_thumb_680CD8CD.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;There are a few minor differences between this and the previous diagram.&amp;nbsp; In particular, the expression computed by the aggregate is added by a separate projection (omitted for clarity before), and the SQL-92 join syntax means the join predicate is more tightly bound to the logical join.&amp;nbsp; Rewriting the query using SQL-89 syntax highlights the second difference, producing a tree closer to the cartesian product followed by a relational SELECT:&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:#008000;"&gt;-- Input tree (ISO-89)&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;    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;    Total = &lt;span style="color:#0000ff;"&gt;SUM&lt;/span&gt;(inv.Quantity)&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; &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;    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;    Production.ProductInventory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; inv&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;    inv.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;AND&lt;/span&gt; p.Name &lt;span style="color:#0000ff;"&gt;LIKE&lt;/span&gt; N&lt;span style="color:#006080;"&gt;'[A-G]%'&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;GROUP&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:#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;OPTION&lt;/span&gt; (RECOMPILE, QUERYTRACEON 8605);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_4C1BA3D5.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="Input Tree SQL-89 Syntax" border="0" alt="Input Tree SQL-89 Syntax" width="644" height="670" src="http://sqlblog.com/blogs/paul_white/image_thumb_3434BCAF.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 align="left"&gt;Simplification&lt;/h3&gt;

&lt;p align="left"&gt;The first step of the optimization pipeline we will look at is simplification, where the optimizer looks to rewrite parts of the logical tree to remove redundancies and move logical operations around a bit to help later stages.&amp;nbsp; Major activities that occur around the time simplification occurs (I have taken a little artistic licence here to group a few separate stages together) include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;div align="left"&gt;Constant folding&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Domain simplification&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Predicate push-down&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;Join simplification &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="left"&gt;Contradiction detection&lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p align="left"&gt;Constant folding is the process of evaluating an expression during optimization, rather than repeatedly at execution time.&amp;nbsp; In the following example, the complex expression in the WHERE clause can safely be evaluated early, resulting in “WHERE p.Name LIKE ‘D%’:&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; 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; p.Name &lt;span style="color:#0000ff;"&gt;LIKE&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;SUBSTRING&lt;/span&gt;(&lt;span style="color:#0000ff;"&gt;LEFT&lt;/span&gt;(&lt;span style="color:#0000ff;"&gt;CHAR&lt;/span&gt;(ASCII(&lt;span style="color:#0000ff;"&gt;CHAR&lt;/span&gt;(68))), 1) + &lt;span style="color:#006080;"&gt;'%'&lt;/span&gt;, 1, 2);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;Domain simplification enables the optimizer to reason about the range of valid values a column or expression can take.&amp;nbsp; In the next query, the only value for Product ID that logically matches all the predicates in the extended WHERE clause is the single value ‘400’.&amp;nbsp; The query plan shows that simplification has reduced the three predicates to a single equality predicate, “WHERE p.ProductID = 400”:&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; (10) * &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; p.ProductID &lt;span style="color:#0000ff;"&gt;BETWEEN&lt;/span&gt; 300 &lt;span style="color:#0000ff;"&gt;AND&lt;/span&gt; 400&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;AND&lt;/span&gt; p.ProductID &lt;span style="color:#0000ff;"&gt;BETWEEN&lt;/span&gt; 200 &lt;span style="color:#0000ff;"&gt;AND&lt;/span&gt; 500&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;AND&lt;/span&gt; p.ProductID &lt;span style="color:#0000ff;"&gt;BETWEEN&lt;/span&gt; 400 &lt;span style="color:#0000ff;"&gt;AND&lt;/span&gt; 600;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;Predicate push-down involves pushing relational SELECTs (logically filtering rows based on a predicate) down the logical tree toward the leaves.&amp;nbsp; The heuristic here is that this is always a good thing to do (at least as far as expressions that reference a column are concerned) because it eliminates rows early, reducing the number for later logical operations.&amp;nbsp; Moving SELECTs closer to the logical GETs (reading tables) also helps index and computed-column matching.&lt;/p&gt;

&lt;p align="left"&gt;Join simplification allows the optimizer to remove unnecessary joins, convert logical outer joins to simpler inner joins where the NULLs introduces by the outer join are later rejected by another feature in the query, and remove provably empty subexpressions.&amp;nbsp; This is an incredibly powerful optimizer facility, with its roots again in relational theory.&amp;nbsp; Rob Farley has written (&lt;a target="_blank" href="http://www.amazon.com/SQL-Server-MVP-Deep-Dives/dp/1935182048"&gt;SQL Server MVP Deep Dives&lt;/a&gt;) and presented (&lt;a title="http://bit.ly/SimpleRob" href="http://bit.ly/SimpleRob"&gt;http://bit.ly/SimpleRob&lt;/a&gt;) about ways to use this feature effectively.&amp;nbsp; In particular, he talks about how to write views to benefit from compile-time simplification, and avoid the views-on-views-on-views problem.&amp;nbsp; The following queries illustrate some of the join simplifications:&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:#008000;"&gt;-- Remove unnecessary joins&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;    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;SUM&lt;/span&gt;(th.ActualCost)&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.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th&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.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p &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;GROUP&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;    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:#008000;"&gt;-- Outer join to join (null rejection)&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;&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;    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;WHERE&lt;/span&gt; th.ProductID &amp;lt; 10;&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;-- Complex example combining multiple simplifications&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;WITH&lt;/span&gt; Complex &lt;span style="color:#0000ff;"&gt;AS&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;/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;        pc.ProductCategoryID, pc.Name &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; CatName,&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;        ps.ProductSubcategoryID, ps.Name &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; SubCatName,&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, p.Name &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; ProductName,&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.Color, p.ListPrice, p.ReorderPoint,&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;        pm.Name &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; ModelName, pm.ModifiedDate&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.ProductCategory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; pc&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;FULL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; Production.ProductSubcategory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; ps &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;        ps.ProductCategoryID = pc.ProductCategoryID&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;FULL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; Production.Product &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; p &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.ProductSubcategoryID = ps.ProductSubcategoryID&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;FULL&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;JOIN&lt;/span&gt; Production.ProductModel &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; pm &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;        pm.ProductModelID = p.ProductModelID&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;/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; c.ProductID, c.ProductName&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; Complex &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; c&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; c.ProductName &lt;span style="color:#0000ff;"&gt;LIKE&lt;/span&gt; N&lt;span style="color:#006080;"&gt;'G%'&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;That last example, with three full outer joins in a CTE (in-line view), simplifies down to a simple index seek on one table.&lt;/p&gt;

&lt;h3 align="left"&gt;Cardinality Estimation&lt;/h3&gt;

&lt;p align="left"&gt;The optimizer only has direct information about table cardinality (total number of rows) for base tables.&amp;nbsp; Statistics can provide additional histograms and frequency statistics, but again these are only maintained for base tables.&amp;nbsp; To help the optimizer choose between competing strategies later on, it is essential to know how many rows are expected at each node in the logical tree, not just at the GET leaf nodes.&amp;nbsp; In addition, cardinality estimates above the leaves can be used to choose an initial join order (assuming the query contains several joins).&lt;/p&gt;

&lt;p align="left"&gt;Cardinality estimation computes expected cardinality and distribution statistics for each node, working up from the leaves one node at a time.&amp;nbsp; The logic used to create these derived estimates and statistics uses a model that makes certain assumptions about your data.&amp;nbsp; For example, where the distribution of values is unknown, it is assumed to be uniform across the whole range of potential values.&lt;/p&gt;

&lt;p align="left"&gt;You will generally get more accurate cardinality estimates if your queries fit the model used, and, as with many things in the wider optimizer, simple and relational is best.&amp;nbsp; Complex expressions, unusual or novel techniques, and using non-relational features often means cardinality estimation has to resort to guessing.&lt;/p&gt;

&lt;p align="left"&gt;Most of the choices made by the optimizer are driven by cardinality estimation, so if these are wrong, any good execution plans you might see are highly likely to be down to pure luck.&lt;/p&gt;

&lt;h3 align="left"&gt;Trivial Plan&lt;/h3&gt;

&lt;p align="left"&gt;The next stage of the optimizer pipeline is Trivial Plan, which is a fast path to avoid the time and effort involved in full cost-based optimization, and only applies to logical query trees that have a clear and obvious ‘best’ execution plan.&amp;nbsp; The details of which types of query can benefit from Trivial Plan change frequently, but things like joins, subqueries, and inequality predicates generally prevent this optimization.&amp;nbsp; The queries below show some examples where Trivial Plan can, and cannot be applied:&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:#008000;"&gt;-- Trivial plan&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; p.ProductID&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; p.Name = N&lt;span style="color:#006080;"&gt;'Blade'&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:#008000;"&gt;-- Still trivial&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;    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;    RowNumber =&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;        ROW_NUMBER() &lt;span style="color:#0000ff;"&gt;OVER&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;ORDER&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;BY&lt;/span&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; p.Name &lt;span style="color:#0000ff;"&gt;LIKE&lt;/span&gt; N&lt;span style="color:#006080;"&gt;'[A-G]%'&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:#008000;"&gt;-- 'Subquery' prevents a trivial plan&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;SELECT&lt;/span&gt; p.ProductID)&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; p.Name = N&lt;span style="color:#006080;"&gt;'Blade'&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:#008000;"&gt;-- Inequality&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; 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;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; p.Name &amp;lt;&amp;gt; N&lt;span style="color:#006080;"&gt;'Blade'&lt;/span&gt;;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;One interesting case where a Trivial Plan is not applied is where the estimated cost of the Trivial Plan query exceeds the configured ‘cost threshold for parallelism’.&amp;nbsp; The reasoning here is that any trivial plan that would qualify for a parallel plan suddenly presents a choice, and a choice means the query is no longer ‘trivial’, and we have to go through full cost-based optimization.&amp;nbsp; Taking this to the extreme, a SQL Server instance with the parallelism cost threshold set to zero will never produce a trivial plan (since all queries have a cost greater than zero).&amp;nbsp; At the risk of stating the slightly obvious, note that a plan produced at this stage will always be serial.&amp;nbsp; For experimentation only, the trivial plan stage can also be disabled using trace flag 8757.&lt;/p&gt;

&lt;p align="left"&gt;You can check whether a query used a trivial plan or not by inspecting the Properties window in SSMS, and clicking on the root node of the graphical query plan.&amp;nbsp; The ‘optimization level’ property will show ‘Trivial’ or ‘Full’ – the latter shows that a trip through full cost-based optimization was required.&amp;nbsp; If a query results in a trivial plan, a physical execution plan is produced and the optimization process ends here.&lt;/p&gt;

&lt;h3 align="left"&gt;End of Part 1&lt;/h3&gt;

&lt;p align="left"&gt;The original slides and demo code are contained in the zip file attached below.&amp;nbsp; Links to other parts of this series: &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-2.aspx"&gt;Part 2&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/04/29/query-optimizer-deep-dive-part-3.aspx"&gt;Part 3&lt;/a&gt; &lt;a target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2012/05/01/query-optimizer-deep-dive-part-4.aspx"&gt;Part 4&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&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;img src="http://sqlblog.com/aggbug.aspx?PostID=43025" width="1" height="1"&gt;</description><enclosure url="http://sqlblog.com/blogs/paul_white/attachment/43025.ashx" length="1955066" type="application/x-zip-compressed" /><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Costing/default.aspx">Costing</category><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+Optimizer/default.aspx">Query Optimizer</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/Undocumented/default.aspx">Undocumented</category></item><item><title>Fun with Aggregates</title><link>http://sqlblog.com/blogs/paul_white/archive/2012/03/12/fun-with-aggregates.aspx</link><pubDate>Sun, 11 Mar 2012 15:49:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:42234</guid><dc:creator>Paul White</dc:creator><slash:comments>14</slash:comments><comments>http://sqlblog.com/blogs/paul_white/comments/42234.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/paul_white/commentrss.aspx?PostID=42234</wfw:commentRss><description>&lt;p align="left"&gt;There are interesting things to be learned from even the simplest queries.&amp;nbsp; For example, imagine you are given the task of writing a query to list AdventureWorks product names where the product has at least one entry in the transaction history table, but fewer than ten.&lt;/p&gt;  &lt;p&gt;One possible query to meet that specification is:&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;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;GROUP&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.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;    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;HAVING&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(*) &amp;lt; 10;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;That query correctly returns 23 rows (execution plan and data sample shown below):&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_35242B39.png"&gt;&lt;img style="display:inline;" title="image" alt="image" width="604" height="323" src="http://sqlblog.com/blogs/paul_white/image_thumb_31C29391.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The execution plan looks a bit different from the written form of the query: the base tables are accessed in reverse order, and the aggregation is performed before the join.&amp;nbsp; The general idea is to read all rows from the history table, compute the count of rows grouped by ProductID, merge join the results to the Product table on ProductID, and finally filter to only return rows where the count is less than ten.&lt;/p&gt;

&lt;p align="left"&gt;This ‘fully-optimized’ plan has an estimated cost of around &lt;b&gt;0.33&lt;/b&gt; units.&amp;nbsp; The reason for the quote marks there is that this plan is not quite as optimal as it could be – surely it would make sense to push the Filter down past the join too?&amp;nbsp; To answer that, let’s look at some other ways to formulate this query.&amp;nbsp; This being SQL, there are any number of ways to write logically-equivalent query specifications, so we’ll just look at a couple of interesting ones.&amp;nbsp; The first query is an attempt to reverse-engineer T-SQL from the optimized query plan shown above.&amp;nbsp; It joins the result of pre-aggregating the history table to the Product table before filtering:&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:300px;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; 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;&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;/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;        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;        cnt = 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.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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;GROUP&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;        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;AS&lt;/span&gt; q1&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;JOIN&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;ON&lt;/span&gt; p.ProductID = q1.ProductID&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;    q1.cnt &amp;lt; 10;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Perhaps a little surprisingly, we get a slightly different execution plan:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_20FAE8E3.png"&gt;&lt;img style="display:inline;" title="image" alt="image" width="605" height="220" src="http://sqlblog.com/blogs/paul_white/image_thumb_5E3B9DAA.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The results are the same (23 rows) but this time the Filter is pushed below the join!&amp;nbsp; The optimizer chooses nested loops for the join, because the cardinality estimate for rows passing the Filter is a bit low (estimate 1 versus 23 actual), though you can force a merge join with a hint and the Filter still appears below the join.&amp;nbsp; In yet another variation, the &amp;lt; 10 predicate can be ‘manually pushed’ by specifying it in a HAVING clause in the “q1” sub-query instead of in the WHERE clause as written above.&lt;/p&gt;

&lt;p align="left"&gt;The reason this predicate can be pushed past the join in this query form, but not in the original formulation is simply an optimizer limitation – it does make efforts (primarily during the simplification phase) to encourage logically-equivalent query specifications to produce the same execution plan, but the implementation is not completely comprehensive.&lt;/p&gt;

&lt;p align="left"&gt;Moving on to a second example, the following query specification results from phrasing the requirement as “list the products where there exists fewer than ten correlated rows in the history table”:&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; 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;span style="color:#0000ff;"&gt;EXISTS&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;/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;    &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; 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;HAVING&lt;/span&gt; COUNT_BIG(*) &amp;lt; 10&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;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Unfortunately, this query produces an incorrect result (86 rows):&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_222F5BF5.png"&gt;&lt;img style="display:inline;" title="image" alt="image" width="586" height="195" src="http://sqlblog.com/blogs/paul_white/image_thumb_786BE101.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The problem is that it lists products with no history rows, though the reasons are interesting.&amp;nbsp; The COUNT_BIG(*) in the EXISTS clause is a scalar aggregate (meaning there is no GROUP BY clause) and scalar aggregates always produce a value, even when the input is an empty set.&amp;nbsp; In the case of the COUNT aggregate, the result of aggregating the empty set is zero (the other standard aggregates produce a NULL).&amp;nbsp; To make the point really clear, let’s look at product 709, which happens to be one for which no history rows exist:&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:#008000;"&gt;-- Scalar aggregate&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; 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.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; th.ProductID = 709;&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;nbsp;&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;-- Vector aggregate&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; 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; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; th.ProductID = 709&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;GROUP&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;BY&lt;/span&gt; th.ProductID;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The estimated execution plans for these two statements are almost identical:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_11873B3A.png"&gt;&lt;img style="display:inline;" title="image" alt="image" width="374" height="105" src="http://sqlblog.com/blogs/paul_white/image_thumb_7A0C8708.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;You might expect the Stream Aggregate to have a Group By for the second statement, but this is not the case.&amp;nbsp; The query includes an equality comparison to a constant value (709), so all qualified rows are guaranteed to have the same value for ProductID and the Group By is optimized away.&lt;/p&gt;

&lt;p align="left"&gt;In fact there are some minor differences between the two plans (the first is auto-parameterized and qualifies for trivial plan, whereas the second is not auto-parameterized and requires cost-based optimization), but there is nothing to indicate that one is a scalar aggregate and the other is a vector aggregate.&amp;nbsp; This is something I would like to see exposed in show plan so I &lt;a target="_blank" href="https://connect.microsoft.com/SQLServer/feedback/details/730458/enhance-show-plan-to-distinguish-scalar-vector-aggregates"&gt;suggested it on Connect&lt;/a&gt;.&amp;nbsp; Anyway, the results of running the two queries show the difference at runtime:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_2570A803.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" width="367" height="160" src="http://sqlblog.com/blogs/paul_white/image_thumb_436EB5F7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The &lt;b&gt;scalar aggregate&lt;/b&gt; (no GROUP BY) returns a result of zero, whereas the &lt;b&gt;vector aggregate&lt;/b&gt; (with a GROUP BY clause) returns nothing at all.&amp;nbsp; Returning to our EXISTS query, we could ‘fix’ it by changing the HAVING clause to reject rows where the scalar aggregate returns zero:&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; 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;span style="color:#0000ff;"&gt;EXISTS&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;/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;    &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; 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;HAVING&lt;/span&gt; COUNT_BIG(*) &lt;span style="color:#0000ff;"&gt;BETWEEN&lt;/span&gt; 1 &lt;span style="color:#0000ff;"&gt;AND&lt;/span&gt; 9&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;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The query now returns the correct 23 rows:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_4C7B4178.png"&gt;&lt;img style="display:inline;" title="image" alt="image" width="590" height="198" src="http://sqlblog.com/blogs/paul_white/image_thumb_65969BB0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;Unfortunately, the execution plan is less efficient now – it has an estimated cost of &lt;b&gt;0.78&lt;/b&gt; compared to &lt;b&gt;0.33&lt;/b&gt; for the earlier plans.&amp;nbsp; Let’s try adding a redundant GROUP BY instead of changing the HAVING clause:&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; 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;span style="color:#0000ff;"&gt;EXISTS&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;/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;    &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; 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;GROUP&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;BY&lt;/span&gt; th.ProductID&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;HAVING&lt;/span&gt; COUNT_BIG(*) &amp;lt; 10&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;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Not only do we now get correct results (23 rows), this is the execution plan:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_26E19E4A.png"&gt;&lt;img style="display:inline;" title="image" alt="image" width="607" height="192" src="http://sqlblog.com/blogs/paul_white/image_thumb_3FFCF882.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;I like to compare that plan to quantum physics: if you don’t find it shocking, you haven’t understood it properly :)&amp;nbsp; The simple addition of a redundant GROUP BY has resulted in the EXISTS form of the query being transformed into exactly the same optimal plan we found earlier.&amp;nbsp; What’s more, in SQL Server 2008 and later, we can replace the odd-looking GROUP BY with an explicit GROUP BY on the empty set:&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; 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;span style="color:#0000ff;"&gt;EXISTS&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;/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;    &lt;span style="color:#0000ff;"&gt;FROM&lt;/span&gt; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; 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;GROUP&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;    &lt;span style="color:#0000ff;"&gt;HAVING&lt;/span&gt; COUNT_BIG(*) &amp;lt; 10&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;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p align="left"&gt;I offer that as an alternative because some people find it more intuitive (and it perhaps has more geek value too).&amp;nbsp; Whichever way you prefer, it’s rather satisfying to note that the result of the sub-query does not exist for a particular correlated value where a vector aggregate is used (the scalar COUNT aggregate always returns a value, even if zero, so it always ‘EXISTS’ regardless which ProductID is logically being evaluated).&lt;/p&gt;

&lt;p align="left"&gt;The following query forms also produce the optimal plan and correct results, so long as a vector aggregate is used (you can probably find more equivalent query forms):&lt;/p&gt;

&lt;h4 align="left"&gt;WHERE Clause&lt;/h4&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;(&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; 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.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th&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; 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: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;GROUP&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:#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;lt; 10;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;h4&gt;APPLY&lt;/h4&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; 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;CROSS&lt;/span&gt; APPLY&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;/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;span style="color:#0000ff;"&gt;NULL&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;FROM&lt;/span&gt; Production.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; 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;GROUP&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;    &lt;span style="color:#0000ff;"&gt;HAVING&lt;/span&gt; COUNT_BIG(*) &amp;lt; 10&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;AS&lt;/span&gt; ca (&lt;span style="color:#0000ff;"&gt;dummy&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;h4&gt;FROM Clause&lt;/h4&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:300px;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; q1.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;&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;/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;        cnt = &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;/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; 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.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; 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: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;GROUP&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:#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;/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;AS&lt;/span&gt; q1 &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;    q1.cnt &amp;lt; 10;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This last example uses SUM(1) instead of COUNT and does not require a vector aggregate…you should be able to work out why :)&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:250px;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; q.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;&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;/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;        cnt = &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;/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;SUM&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: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.TransactionHistory &lt;span style="color:#0000ff;"&gt;AS&lt;/span&gt; th &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; 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: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;/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;AS&lt;/span&gt; q&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; q.cnt &amp;lt; 10;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/paul_white/image_0147FB1C.png"&gt;&lt;img style="display:inline;" title="image" alt="image" width="603" height="196" src="http://sqlblog.com/blogs/paul_white/image_thumb_1A635554.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p align="left"&gt;The semantics of SQL aggregates are rather odd in places.&amp;nbsp; It definitely pays to get to know the rules, and to be careful to check whether your queries are using scalar or vector aggregates.&amp;nbsp; As we have seen, query plans do not show in which ‘mode’ an aggregate is running and getting it wrong can cause poor performance, wrong results, or both.&lt;/p&gt;

&lt;p&gt;© 2012 Paul White&lt;/p&gt;

&lt;p&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=42234" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Aggregates/default.aspx">Aggregates</category><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+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/TSQL/default.aspx">TSQL</category></item><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:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:41097</guid><dc:creator>Paul White</dc:creator><slash:comments>16</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 target="_blank" href="http://sqlblog.com/blogs/paul_white/image_3C27CA3C.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="Index Seek on LIKE" border="0" alt="Index Seek on LIKE" width="403" height="334" src="http://sqlblog.com/blogs/paul_white/image_thumb_4B7B2C4B.png"&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;nbsp; 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;nbsp;&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 target="_blank" href="http://sqlblog.com/blogs/paul_white/image_4FA50410.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="Dynamic Index Seek" border="0" alt="Dynamic Index Seek" width="632" height="483" src="http://sqlblog.com/blogs/paul_white/image_thumb_5362A8E0.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The plan now contains an extra Constant Scan,&amp;nbsp; a Compute Scalar and a Nested Loops Join.&amp;nbsp; These operators are interesting because they have zero cost estimates: no CPU, no I/O, nothing.&amp;nbsp; 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;nbsp; 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;nbsp; The Compute Scalar defines expressions to describe the covering seek range (using the runtime value of the @Like variable).&amp;nbsp; 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;b&gt;LikeRangeStart&lt;/b&gt;, &lt;b&gt;LikeRangeEnd&lt;/b&gt;, and &lt;b&gt;LikeRangeInfo&lt;/b&gt;.&amp;nbsp; The first two functions describe the range as an open interval.&amp;nbsp; 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;nbsp; The lower tooltip shows the seek on the open interval described by the result of &lt;i&gt;LikeRangeStart&lt;/i&gt; and &lt;i&gt;LikeRangeEnd&lt;/i&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;nbsp;&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 target="_blank" href="http://sqlblog.com/blogs/paul_white/image_506D442D.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="Dynamic Seek With IN and OR" border="0" alt="Dynamic Seek With IN and OR" width="647" height="574" src="http://sqlblog.com/blogs/paul_white/image_thumb_17FF1D55.png"&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;nbsp; 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;i&gt;LikeRangeInfo&lt;/i&gt; function).&amp;nbsp; This time, we see the decimal representation of these flags, which happens to be 62 for an equality comparison.&amp;nbsp; 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;nbsp; 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;nbsp; Anyway, for each disjoint range produced, the Nested Loops Join drives a new seek of the Clustered Index.&amp;nbsp; 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;nbsp;&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;nbsp;&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;nbsp;&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 target="_blank" href="http://sqlblog.com/blogs/paul_white/image_02C0F1E0.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="GetRangeThroughConvert" border="0" alt="GetRangeThroughConvert" width="702" height="461" src="http://sqlblog.com/blogs/paul_white/image_thumb_2DB8DFE5.png"&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;nbsp; 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;b&gt;GetRangeThroughConvert&lt;/b&gt;, is responsible for determining the the range of DATETIME2 values covered by the DATE variable @date, and the informational flags needed.&amp;nbsp; 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;nbsp; 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;nbsp; 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 target="_blank" href="http://sqlblog.com/blogs/paul_white/image_1C850242.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="GetRangeWithMismatchedTypes" border="0" alt="GetRangeWithMismatchedTypes" width="541" height="351" src="http://sqlblog.com/blogs/paul_white/image_thumb_6EB7397C.png"&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;nbsp; 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;nbsp; 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;b&gt;GetRangeThroughConvert&lt;/b&gt; on the string expression and &lt;b&gt;GetRangeThroughConvert&lt;/b&gt; &lt;i&gt;on the result of&lt;/i&gt; &lt;b&gt;GetRangeWithMismatchedTypes&lt;/b&gt; applied to the result of the GETDATE function.&amp;nbsp; 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;nbsp; See if you can work out all the conversions necessary for this query, using the rules of data type precedence.&amp;nbsp; 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 target="_blank" href="http://sqlblog.com/blogs/paul_white/image_2BF7EE44.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="Merge Interval Implcit Conversions" border="0" alt="Merge Interval With Everything" width="651" height="450" src="http://sqlblog.com/blogs/paul_white/image_thumb_1AC410A1.png"&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;nbsp; 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;nbsp; 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;nbsp; The problem is that these hidden implicit conversions can result in inaccurate cardinality and distribution estimations at any stage of the plan.&amp;nbsp; So, even if you get a seek, the plan might be way off overall.&amp;nbsp; If that isn’t persuasive enough, consider this: will having hidden nested range calculations improve your chances of getting a good query plan?&amp;nbsp; Probably not, no.&amp;nbsp; Be very aware of types, and in particular of the types returned by functions and expressions.&amp;nbsp; 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;i&gt;&lt;/i&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;nbsp; 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 target="_blank" href="http://sqlblog.com/blogs/paul_white/archive/2011/07/19/join-performance-implicit-conversions-and-residuals.aspx"&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><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Conversions/default.aspx">Conversions</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/Seeks/default.aspx">Seeks</category></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>31</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:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:40125</guid><dc:creator>Paul White</dc:creator><slash:comments>11</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><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Aggregates/default.aspx">Aggregates</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/Query+Plans/default.aspx">Query Plans</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Transformations/default.aspx">Transformations</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/TSQL/default.aspx">TSQL</category></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>18</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/Statistics/default.aspx">Statistics</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/Undocumented/default.aspx">Undocumented</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/Indexes/default.aspx">Indexes</category><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/Memory+Grant/default.aspx">Memory Grant</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/Query+Plans/default.aspx">Query Plans</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/Spool/default.aspx">Spool</category><category domain="http://sqlblog.com/blogs/paul_white/archive/tags/Undocumented/default.aspx">Undocumented</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/Indexes/default.aspx">Indexes</category><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/Performance/default.aspx">Performance</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/Undocumented/default.aspx">Undocumented</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></channel></rss>
