<?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>The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx</link><description>A question that often comes up on the forums is how to get the first or last row from each group of records in a table. This post describes a clever query plan optimisation that SQL Server can use for these types of query. As a simple example, based on</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>The Segment and Sequence Project Iterators</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#27327</link><pubDate>Tue, 27 Jul 2010 11:51:10 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:27327</guid><dc:creator>Page Free Space - Paul White</dc:creator><description>&lt;p&gt;In my last post I promised to cover the Segment iterator in more detail, so here we go. Segment The Segment&lt;/p&gt;
</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#27329</link><pubDate>Tue, 27 Jul 2010 11:59:20 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:27329</guid><dc:creator>Uri Dimant</dc:creator><description>&lt;p&gt;Hi Paul &lt;/p&gt;
&lt;p&gt;Well as you said every approach should be tested. Cross apply operastor resturns &amp;nbsp;(SQL Server 2005 sp3)&lt;/p&gt;
&lt;p&gt;Table 'Worktable'. Scan count 441, logical reads 3055, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;/p&gt;
&lt;p&gt;Table 'ProductInventory'. Scan count 2, logical reads 18, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;/p&gt;
&lt;p&gt;But using ROW_NUMBER() function&lt;/p&gt;
&lt;p&gt;WITH cte&lt;/p&gt;
&lt;p&gt;AS&lt;/p&gt;
&lt;p&gt;(&lt;/p&gt;
&lt;p&gt;SELECT &amp;nbsp;ProductID,INV.Shelf, &lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; INV.Bin, &lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Quantity,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; min_qty= ROW_NUMBER() OVER (PARTITION&lt;/p&gt;
&lt;p&gt; BY INV.Shelf,INV.Bin ORDER BY &amp;nbsp;INV.Quantity ) &amp;nbsp;&lt;/p&gt;
&lt;p&gt;FROM &amp;nbsp; &amp;nbsp;Production.ProductInventory INV &amp;nbsp;&lt;/p&gt;
&lt;p&gt;) SELECT * FROM cte WHERE min_qty=1&lt;/p&gt;
&lt;p&gt;ORDER &amp;nbsp; By &amp;nbsp; &amp;nbsp; &amp;nbsp; Shelf,Bin&lt;/p&gt;
&lt;p&gt;Table 'ProductInventory'. Scan&lt;/p&gt;
&lt;p&gt;count 1, logical reads 9, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;/p&gt;
&lt;p&gt;I agree with you that the both query produced different results.but testing is always goos way to choose right query.&lt;/p&gt;
&lt;p&gt;Thanks.&lt;/p&gt;
&lt;p&gt;I agree with&lt;/p&gt;
</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#27331</link><pubDate>Tue, 27 Jul 2010 12:19:10 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:27331</guid><dc:creator>mjswart</dc:creator><description>&lt;p&gt;Awesome Post. Looking forward to the next one about the segment operator.&lt;/p&gt;
</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#27337</link><pubDate>Tue, 27 Jul 2010 14:38:58 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:27337</guid><dc:creator>Paul White</dc:creator><description>&lt;p&gt;Thanks for the comments Uri and Michael.&lt;/p&gt;
</description></item><item><title>Inside the Optimiser: Constructing a Plan</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#27373</link><pubDate>Wed, 28 Jul 2010 17:17:54 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:27373</guid><dc:creator>Page Free Space - Paul White</dc:creator><description>&lt;p&gt;For today’s entry, I thought we might take a look at how the optimiser builds an executable plan using&lt;/p&gt;
</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#48821</link><pubDate>Mon, 22 Apr 2013 21:23:40 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:48821</guid><dc:creator>Ian Turner</dc:creator><description>&lt;p&gt;Hi Paul,&lt;/p&gt;
&lt;p&gt;It looks like the optimizer will not apply the segment top (Generate Group By Apply) optimization where one of the inputs is itself subject to the optimization. Any idea why? Unfortunately, the unoptimized query plan is much worse than the ROW_NUMBER() alternative.&lt;/p&gt;
&lt;p&gt;This query should demonstrate the problem. It takes your query and then finds the lowest-numbered location for the affected products.&lt;/p&gt;
&lt;p&gt;WITH InnerQuery1 AS&lt;/p&gt;
&lt;p&gt;(&lt;/p&gt;
&lt;p&gt;SELECT &amp;nbsp;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV.Shelf,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;min_qty = MIN(INV.Quantity)&lt;/p&gt;
&lt;p&gt;FROM&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;Production.ProductInventory AS INV&lt;/p&gt;
&lt;p&gt;GROUP BY&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV.Shelf&lt;/p&gt;
&lt;p&gt;), OuterQuery1 AS&lt;/p&gt;
&lt;p&gt;(&lt;/p&gt;
&lt;p&gt;SELECT&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV2.ProductID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV2.LocationID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV2.Shelf,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV2.Bin,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV2.Quantity&lt;/p&gt;
&lt;p&gt;FROM InnerQuery1 AS IQ&lt;/p&gt;
&lt;p&gt;INNER JOIN Production.ProductInventory AS INV1 ON&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV1.Shelf = IQ.Shelf&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;AND INV1.Quantity = IQ.min_qty&lt;/p&gt;
&lt;p&gt;INNER JOIN Production.ProductInventory AS INV2 ON&lt;/p&gt;
&lt;p&gt;	INV1.ProductID = INV2.ProductID&lt;/p&gt;
&lt;p&gt;), InnerQuery2 AS&lt;/p&gt;
&lt;p&gt;(&lt;/p&gt;
&lt;p&gt;SELECT&lt;/p&gt;
&lt;p&gt;	OQ.ProductID,&lt;/p&gt;
&lt;p&gt;	min_loc = MIN(LocationId)&lt;/p&gt;
&lt;p&gt;FROM&lt;/p&gt;
&lt;p&gt;	OuterQuery1 AS OQ&lt;/p&gt;
&lt;p&gt;GROUP BY&lt;/p&gt;
&lt;p&gt;	OQ.ProductID&lt;/p&gt;
&lt;p&gt;), OuterQuery2 AS&lt;/p&gt;
&lt;p&gt;(&lt;/p&gt;
&lt;p&gt;SELECT&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV.ProductID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV.LocationID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV.Shelf,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV.Bin,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;INV.Quantity&lt;/p&gt;
&lt;p&gt;FROM OuterQuery1 AS INV&lt;/p&gt;
&lt;p&gt;INNER JOIN InnerQuery2 AS IQ ON&lt;/p&gt;
&lt;p&gt;	INV.ProductID = IQ.ProductID&lt;/p&gt;
&lt;p&gt;	AND INV.LocationID = IQ.min_loc&lt;/p&gt;
&lt;p&gt;)&lt;/p&gt;
&lt;p&gt;SELECT * FROM OuterQuery2&lt;/p&gt;
&lt;p&gt;What I would expect to get would be the query plan for InnerQuery2 with a segment top attached. But instead, OuterQuery1 is optimized as expected but OuterQuery2 is not, resulting in not one but two executions of InnerQuery2.&lt;/p&gt;</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#48827</link><pubDate>Tue, 23 Apr 2013 01:46:05 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:48827</guid><dc:creator>Paul White</dc:creator><description>&lt;p&gt;Hi Ian,&lt;/p&gt;
&lt;p&gt;The fundamental issue is not specific to GenGbApplySimple, it is a limitation in the way the optimizer reasons about table expressions. &lt;/p&gt;
&lt;p&gt;Where the input table expressions are base tables, there is no issue because GenGbApplySimple is written to work with two references to the same object.&lt;/p&gt;
&lt;p&gt;When a CTE (or, equivalently, a view) is used to provide the references, the CTE (or view) is expanded into the query tree before optimization starts and the connection to the original reference is lost.&lt;/p&gt;
&lt;p&gt;This general feature of query processing can cause unexpectedly poor performance in a number of scenarios where query writers expect the CTE to be evaluated just once (see &lt;a rel="nofollow" target="_new" href="http://connect.microsoft.com/SQLServer/feedback/details/218968/"&gt;http://connect.microsoft.com/SQLServer/feedback/details/218968/&lt;/a&gt; and the linked duplicates of that item for more background).&lt;/p&gt;
&lt;p&gt;One workaround is to materialize the CTE manually:&lt;/p&gt;
&lt;p&gt;-- Lowest quantity per shelf and bin&lt;/p&gt;
&lt;p&gt;SELECT&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;inv.ProductID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;inv.LocationID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;inv.Shelf,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;inv.Bin,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;inv.Quantity&lt;/p&gt;
&lt;p&gt;INTO #temp&lt;/p&gt;
&lt;p&gt;FROM Production.ProductInventory AS inv&lt;/p&gt;
&lt;p&gt;WHERE &lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;inv.Quantity =&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;(&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;SELECT &amp;nbsp;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;MinQty = MIN(inv2.Quantity)&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;FROM&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Production.ProductInventory AS inv2&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;WHERE&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;inv2.Shelf = inv.Shelf&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;AND inv2.Bin = inv.Bin&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;);&lt;/p&gt;
&lt;p&gt;-- Lowest location ID per product&lt;/p&gt;
&lt;p&gt;SELECT&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;t.ProductID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;t.LocationID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;t.Shelf,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;t.Bin,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;t.Quantity&lt;/p&gt;
&lt;p&gt;FROM #temp AS t&lt;/p&gt;
&lt;p&gt;WHERE&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;t.LocationID = (SELECT MIN(t2.LocationID) FROM #temp AS t2 WHERE t2.ProductID = t.ProductID);&lt;/p&gt;
</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#48838</link><pubDate>Tue, 23 Apr 2013 16:11:24 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:48838</guid><dc:creator>Ian Turner</dc:creator><description>&lt;p&gt;Hi Paul,&lt;/p&gt;
&lt;p&gt;Thanks for your thoughts and detailed response.&lt;/p&gt;
&lt;p&gt;It does appear that GenGbApplySimple can be applied to subqueries more complicated than a table expression. For example, it seems to work with a CTE which is just a join of two tables. Consider the following, which takes your original query from the blog post but adds a join with Production.Product in order to filter products by price. It is optimized with Segment Top:&lt;/p&gt;
&lt;p&gt;WITH InnerQuery1 AS&lt;/p&gt;
&lt;p&gt;(&lt;/p&gt;
&lt;p&gt;SELECT INV.ProductID,&lt;/p&gt;
&lt;p&gt;	INV.LocationID,&lt;/p&gt;
&lt;p&gt;	INV.Shelf,&lt;/p&gt;
&lt;p&gt;	INV.Bin,&lt;/p&gt;
&lt;p&gt;	INV.Quantity&lt;/p&gt;
&lt;p&gt;FROM&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;Production.ProductInventory AS INV&lt;/p&gt;
&lt;p&gt;INNER JOIN Production.Product AS P ON&lt;/p&gt;
&lt;p&gt;	INV.ProductID = P.ProductID&lt;/p&gt;
&lt;p&gt;WHERE P.ListPrice &amp;gt; 10&lt;/p&gt;
&lt;p&gt;), InnerQuery2 AS&lt;/p&gt;
&lt;p&gt;(&lt;/p&gt;
&lt;p&gt;SELECT &amp;nbsp;&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ.Shelf,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ.Bin,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;min_qty = MIN(IQ.Quantity)&lt;/p&gt;
&lt;p&gt;FROM InnerQuery1 IQ&lt;/p&gt;
&lt;p&gt;GROUP BY&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ.Shelf, &lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ.Bin&lt;/p&gt;
&lt;p&gt;)&lt;/p&gt;
&lt;p&gt;SELECT&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ1.ProductID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ1.LocationID,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ1.Shelf,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ1.Bin,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ1.Quantity&lt;/p&gt;
&lt;p&gt;FROM InnerQuery2 AS IQ2&lt;/p&gt;
&lt;p&gt;INNER JOIN InnerQuery1 AS IQ1 ON&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ1.Shelf = IQ2.Shelf&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;AND IQ1.Bin = IQ2.Bin&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;AND IQ1.Quantity = IQ2.min_qty&lt;/p&gt;
&lt;p&gt;ORDER BY&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ1.Shelf,&lt;/p&gt;
&lt;p&gt; &amp;nbsp; &amp;nbsp;IQ1.Bin;&lt;/p&gt;
&lt;p&gt;However, it also seems that there are certain features in the innermost query which block even one application of GenGbApplySimple, however. For example, adding a windowing function such as COUNT(*) OVER () to InnerQuery1 and the outer query seems to block the use of GenGbApplySimple.&lt;/p&gt;
&lt;p&gt;So, is this an issue where the optimizer can detect subexpressions but only below a certain level of complexity? Perhaps it only works where the subexpression can be executed with a trivial plan.&lt;/p&gt;</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#48844</link><pubDate>Wed, 24 Apr 2013 00:08:43 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:48844</guid><dc:creator>Paul White</dc:creator><description>&lt;p&gt;Ian,&lt;/p&gt;
&lt;p&gt;Yes GenGbApplySimple can operate on subtrees more complex than a single table expression - my point was that multiple references to a CTE or view will often result in subtrees where the original reference is lost. &lt;/p&gt;
&lt;p&gt;GenGbApplySimple looks to transform Join(x, GbAgg(x)) -&amp;gt; GbApply(x) so it is obviously essential that the 'x' in that formulation is the same 'x' everywhere. Simple join trees are modelled as an n-ary join inside the optimizer, which helps preserves the 'x' identity in your example.&lt;/p&gt;
&lt;p&gt;Introducing extra query features (like a windowing function) may mean the subtree identity is lost before GenGbApplySimple rewrite gets a look in.As a rule of thumb, anything beyond the complexity of a simple n-ary join is quite likely to prevent the match. Even then, there are additional complexities because not every transform will be explored, and not necessarily in the same order, but you get the general idea I hope.&lt;/p&gt;
</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#48851</link><pubDate>Wed, 24 Apr 2013 14:30:07 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:48851</guid><dc:creator>Ian Turner</dc:creator><description>&lt;p&gt;Yup, thanks for your comments. It appears that an additional requirement is that segment top's subtree expression must have a unique key which is covered by the join condition in the outer query.&lt;/p&gt;</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#48886</link><pubDate>Fri, 26 Apr 2013 01:38:39 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:48886</guid><dc:creator>Paul White</dc:creator><description>&lt;p&gt;I'm not sure what you mean by 'a unique key which is covered by the join condition in the outer query'. Certainly, the logic of the thing is that we are looking for rows matching a single value (with ties) from a segment of the same table expression, but that doesn't really mean the same thing.&lt;/p&gt;
&lt;p&gt;Perhaps a simpler example will make it clearer:&lt;/p&gt;
&lt;p&gt;-- Create a heap copy&lt;/p&gt;
&lt;p&gt;SELECT * INTO #p FROM Production.Product AS p&lt;/p&gt;
&lt;p&gt;-- Segment Apply&lt;/p&gt;
&lt;p&gt;SELECT p.*, x.mlp FROM #p AS p&lt;/p&gt;
&lt;p&gt;JOIN (SELECT p.Color, MIN(p.ListPrice) AS mlp FROM #p AS p GROUP BY p.Color) AS x&lt;/p&gt;
&lt;p&gt;ON p.Color = x.Color;&lt;/p&gt;
&lt;p&gt;-- Same Segment Apply using different syntax&lt;/p&gt;
&lt;p&gt;SELECT *, MIN(p.ListPrice) OVER (PARTITION BY p.Color)&lt;/p&gt;
&lt;p&gt;FROM #p AS p&lt;/p&gt;
&lt;p&gt;WHERE p.Color IS NOT NULL;&lt;/p&gt;
&lt;p&gt;-- Top 1 with ties per segment&lt;/p&gt;
&lt;p&gt;SELECT p.*, x.mlp FROM #p AS p&lt;/p&gt;
&lt;p&gt;JOIN (SELECT p.Color, MIN(p.ListPrice) AS mlp FROM #p AS p GROUP BY p.Color) AS x&lt;/p&gt;
&lt;p&gt;ON p.Color = x.Color AND p.ListPrice = x.mlp;&lt;/p&gt;
</description></item><item><title>re: The “Segment Top” Query Optimisation</title><link>http://sqlblog.com/blogs/paul_white/archive/2010/07/28/the-segment-top-query-optimisation.aspx#49001</link><pubDate>Wed, 08 May 2013 17:50:44 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:49001</guid><dc:creator>Ian Turner</dc:creator><description>&lt;p&gt;Hi Paul, what I meant by this is that it seems like the optimizer cannot use GenGbApplySimple unless it knows that the join condition between the two table expressions is unique. If you drop the unique key on the AdventureWorks tables, you may see that Segment Top no longer applies.&lt;/p&gt;</description></item></channel></rss>