<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Craig Freedman's SQL Server Blog : Top</title><link>http://blogs.msdn.com/craigfr/archive/tags/Top/default.aspx</link><description>Tags: Top</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>More on TOP</title><link>http://blogs.msdn.com/craigfr/archive/2007/08/01/more-on-top.aspx</link><pubDate>Wed, 01 Aug 2007 19:13:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:4171751</guid><dc:creator>craigfr</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.msdn.com/craigfr/comments/4171751.aspx</comments><wfw:commentRss>http://blogs.msdn.com/craigfr/commentrss.aspx?PostID=4171751</wfw:commentRss><description>&lt;P&gt;Last week I wrote about a special case of the TOP operator known as &lt;A title="ROWCOUNT Top" href="http://blogs.msdn.com/craigfr/archive/2007/07/25/rowcount-top.aspx"&gt;ROWCOUNT TOP&lt;/A&gt;.&amp;nbsp; This week I'll take a look at some other interesting TOP scenarios.&amp;nbsp; In general, TOP is a fairly mundane operator.&amp;nbsp; It simply counts and returns the specified number of rows.&amp;nbsp; SQL Server 2005 does include two enhancements to TOP that were not present in SQL Server 2000.&amp;nbsp; First, in SQL Server 2000, we can only specify an integer constant for the number of rows to return.&amp;nbsp; In SQL Server 2005, we can specify an arbitrary expression, including an expression containing T-SQL variables or parameters.&amp;nbsp; Second, SQL Server 2000 only permits TOP in a SELECT statement (though it does support ROWCOUNT TOP in INSERT, UPDATE, and DELETE statements).&amp;nbsp; SQL Server 2005 permits TOP with SELECT, INSERT, UPDATE, and DELETE statements.&lt;/P&gt;
&lt;P&gt;In this post, I'm going to focus only on some simple examples involving SELECT statements.&amp;nbsp; Let's create a small table to get started:&lt;/P&gt;
&lt;P&gt;CREATE TABLE T (A INT, B INT)&lt;BR&gt;CREATE CLUSTERED INDEX TA ON T(A)&lt;/P&gt;
&lt;P mce_keep="true"&gt;SET NOCOUNT ON&lt;BR&gt;DECLARE @i INT&lt;BR&gt;SET @i = 0&lt;BR&gt;WHILE @i &amp;lt; 100&lt;BR&gt;&amp;nbsp; BEGIN&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; INSERT T VALUES (@i, @i)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SET @i = @i + 1&lt;BR&gt;&amp;nbsp; END&lt;BR&gt;SET NOCOUNT OFF&lt;/P&gt;
&lt;P mce_keep="true"&gt;The plan for the simplest possible TOP query should not need any explanation:&lt;/P&gt;
&lt;P&gt;SELECT TOP 5 * FROM T&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp;&amp;nbsp; Executes&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Top(TOP EXPRESSION:((5)))&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[T].[TA]))&lt;/P&gt;
&lt;P mce_keep="true"&gt;TOP is often used in conjunction with ORDER BY.&amp;nbsp; Combining TOP with ORDER BY adds determinism to the set of rows returned.&amp;nbsp; Without the ORDER BY, the set of rows returned depends on the query plan and may even vary from execution to execution.&amp;nbsp; If we have an index to provide order, we continue to get a simple query plan (notice the ORDERED FORWARD keywords):&lt;/P&gt;
&lt;P&gt;SELECT TOP 5 * FROM T ORDER BY A&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp;&amp;nbsp; Executes&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Top(TOP EXPRESSION:((5)))&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[T].[TA]), ORDERED FORWARD)&lt;/P&gt;
&lt;P mce_keep="true"&gt;If we do not have an index to provide order, SQL Server must sort the data:&lt;/P&gt;
&lt;P&gt;SELECT TOP 5 * FROM T ORDER BY B&lt;/P&gt;
&lt;P&gt;Rows&amp;nbsp;&amp;nbsp; Executes&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Sort(TOP 5, ORDER BY:([tempdb].[dbo].[T].[B] ASC))&lt;BR&gt;100&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[T].[TA]))&lt;/P&gt;
&lt;P mce_keep="true"&gt;Note that without an index to find the top 5 rows, SQL Server must scan all 100 rows in the input table.&amp;nbsp; Also note that the sort is actually a "top sort."&amp;nbsp; A top sort generally uses less memory than a regular sort as it only needs to identify and sort the top few rows rather than sorting the entire input.&lt;/P&gt;
&lt;P&gt;Now let's consider what happens if we request the top 5% of rows.&amp;nbsp; To figure out the actual number of rows to return, SQL Server must count all of the rows and calculate 5%.&amp;nbsp; This makes queries that use TOP PERCENT less efficient than queries that use TOP with an absolute row count.&lt;/P&gt;
&lt;P&gt;SELECT TOP 5 PERCENT * FROM T&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp;&amp;nbsp; Executes&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Top(TOP EXPRESSION:((5.000000000000000e+000)) PERCENT)&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Spool&lt;BR&gt;100&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|--Clustered Index Scan(OBJECT:([tempdb].[dbo].[T].[TA]))&lt;/P&gt;
&lt;P mce_keep="true"&gt;Like the prior example, SQL Server must scan all 100 rows in the input table.&amp;nbsp; In this example, SQL Server uses an eager spool which reads and counts all of the input rows before returning any rows.&amp;nbsp; The top then requests the row count from the spool, calculates 5%, and proceeds like any other top.&lt;/P&gt;
&lt;P&gt;If SQL Server must sort, the sort can also provide the count of input rows.&amp;nbsp; However, only a regular sort can provide this count.&amp;nbsp; A top sort must know the number of rows to return from the start.&lt;/P&gt;
&lt;P&gt;SELECT TOP 5 PERCENT * FROM T ORDER BY B&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp;&amp;nbsp; Executes&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Top(TOP EXPRESSION:((5.000000000000000e+000)) PERCENT)&lt;BR&gt;5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Sort(ORDER BY:([tempdb].[dbo].[T].[B] ASC))&lt;BR&gt;100&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[T].[TA]))&lt;/P&gt;
&lt;P mce_keep="true"&gt;TOP WITH TIES is also incompatible with top sort.&amp;nbsp; TOP WITH TIES cannot know for certain how many rows will be returned until the number of ties is determined.&amp;nbsp; For this example, let's add a tie for the fifth row returned:&lt;/P&gt;
&lt;P&gt;INSERT T VALUES (4, 4)&lt;/P&gt;
&lt;P mce_keep="true"&gt;SELECT TOP 5 WITH TIES * FROM T ORDER BY B&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp;&amp;nbsp; Executes&lt;BR&gt;6&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Top(TOP EXPRESSION:((5)))&lt;BR&gt;7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Sort(ORDER BY:([tempdb].[dbo].[T].[B] ASC))&lt;BR&gt;101&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[T].[TA]))&lt;/P&gt;
&lt;P mce_keep="true"&gt;Although the above plan does not seem to reflect that we have a TOP WITH TIES, the argument column of SHOWPLAN_ALL or STATISTICS PROFILE includes this information: &amp;nbsp;"TIE COLUMNS:([T].[B])".&amp;nbsp; This information is also available in the graphical and XML plans on SQL Server 2005.&amp;nbsp; Note that the TOP actually returns one extra row.&amp;nbsp; When a TOP N WITH TIES reaches the Nth row, it keeps a copy of the tie columns for this row (in this example B==4) and compares each subsequent row to that row.&amp;nbsp; As long as there are rows that match, it keeps returning them.&amp;nbsp; Because the top must compare subsequent rows until it finds a non-match to the Nth row, the top retrieves one more row from the sort than it returns.&lt;/P&gt;
&lt;P&gt;Finally, there are a couple of special cases. &amp;nbsp;The optimizer knows that TOP 0 and TOP 0 PERCENT never return any rows and replaces any query plan with a TOP 0 with a constant scan:&lt;/P&gt;
&lt;P&gt;SELECT TOP 0 * FROM T&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp; |--Constant Scan&lt;/P&gt;
&lt;P mce_keep="true"&gt;The optimizer also knows that TOP 100 PERCENT always returns all rows and removes the top operator from the query plan:&lt;/P&gt;
&lt;P&gt;SELECT TOP 100 PERCENT * FROM T&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp; |--Clustered Index Scan(OBJECT:([tempdb].[dbo].[T].[TA]))&lt;/P&gt;
&lt;P mce_keep="true"&gt;Both of these optimizations require that the number of rows is a constant.&amp;nbsp; For example, using an expression that includes a T-SQL variable or parameter will prevent the optimization.&amp;nbsp; Both optimizations also work with INSERT, UPDATE, and DELETE statements.&lt;/P&gt;
&lt;P&gt;Note that using TOP &lt;A title="TOP 100 Percent ORDER BY Considered Harmful." href="http://blogs.msdn.com/queryoptteam/archive/2006/03/24/560396.aspx"&gt;to work around SQL language restrictions on the use of ORDER BY in sub-selects or in views&lt;/A&gt; or &lt;A title="Exploring the secrets of intermediate materialization" href="http://sqlblog.com/blogs/adam_machanic/archive/2006/10/03/267.aspx"&gt;to force specific plan evaluation orders&lt;/A&gt; is not recommended.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4171751" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/craigfr/archive/tags/Top/default.aspx">Top</category></item><item><title>ROWCOUNT Top</title><link>http://blogs.msdn.com/craigfr/archive/2007/07/25/rowcount-top.aspx</link><pubDate>Thu, 26 Jul 2007 00:44:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:4049360</guid><dc:creator>craigfr</dc:creator><slash:comments>6</slash:comments><comments>http://blogs.msdn.com/craigfr/comments/4049360.aspx</comments><wfw:commentRss>http://blogs.msdn.com/craigfr/commentrss.aspx?PostID=4049360</wfw:commentRss><description>If you've looked at any insert, update, or delete plans, including those used in some of my posts, you've probably noticed that nearly all such plans include a top operator.&amp;nbsp; For example, the following update statement yields the following plan: 
&lt;P&gt;CREATE TABLE T (A INT)&lt;BR&gt;INSERT T VALUES (0)&lt;BR&gt;INSERT T VALUES (1)&lt;BR&gt;INSERT T VALUES (2)&lt;/P&gt;
&lt;P mce_keep="true"&gt;UPDATE T SET A = A + 1&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp; Executes&lt;BR&gt;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UPDATE [T] set [A] = [A]+@1&lt;BR&gt;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Table Update(OBJECT:([T]), SET:([T].[A] = [Expr1004]))&lt;BR&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Compute Scalar(DEFINE:([Expr1004]=[T].[A]+[@1]))&lt;BR&gt;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Top(ROWCOUNT est 0)&lt;BR&gt;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Scan(OBJECT:([T]))&lt;/P&gt;
&lt;P&gt;&lt;B&gt;What is that top operator doing right above the table scan?&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;It is a ROWCOUNT top.&amp;nbsp; It is used to implement SET ROWCOUNT functionality.&amp;nbsp; The "est 0" indicates that SET ROWCOUNT was 0 when the query was compiled.&amp;nbsp; (I suppose "est" is short for "estimate" though the value at compilation time has no impact on query optimization or execution.)&amp;nbsp; Recall that a value of 0 means return or update all rows.&amp;nbsp; Since SET ROWCOUNT was 0 at execution time as well, we can see from the STATISTICS PROFILE output that all 3 rows were updated.&lt;/P&gt;
&lt;P&gt;Now try the following:&lt;/P&gt;
&lt;P&gt;SET ROWCOUNT 1&lt;BR&gt;UPDATE T SET A = A + 1&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp; Executes&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UPDATE [T] set [A] = [A]+@1&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Table Update(OBJECT:([T]), SET:([T].[A] = [Expr1004]))&lt;BR&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Compute Scalar(DEFINE:([Expr1004]=[T].[A]+[@1]))&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Top(ROWCOUNT est 0)&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Scan(OBJECT:([T]))&lt;/P&gt;
&lt;P&gt;Although we get the same plan including the ROWCOUNT top with the same "estimate," this time SET ROWCOUNT was 1 at execution time, the top returned only one row from the table scan, and we can see that only 1 row was updated.&lt;/P&gt;
&lt;P&gt;If we force a recompile, we see that the value of the "estimate" changes:&lt;/P&gt;
&lt;P&gt;SET ROWCOUNT 1&lt;BR&gt;UPDATE T SET A = A + 1 OPTION (RECOMPILE)&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp; Executes&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UPDATE T SET A = A + 1 OPTION (RECOMPILE)&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Table Update(OBJECT:([T]), SET:([T].[A] = [Expr1004]))&lt;BR&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Compute Scalar(DEFINE:([Expr1004]=[T].[A]+(1)))&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Top(ROWCOUNT est 1)&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Scan(OBJECT:([T]))&lt;/P&gt;
&lt;P&gt;&lt;B&gt;Why doesn't SQL Server add a ROWCOUNT top to select statements?&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;For example, the following query plan does not include a top yet only returns 1 row:&lt;/P&gt;
&lt;P&gt;SET ROWCOUNT 1&lt;BR&gt;SELECT * FROM T&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp; Executes&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SELECT * FROM T&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Table Scan(OBJECT:([T]))&lt;/P&gt;
&lt;P&gt;SQL Server implements SET ROWCOUNT for select statements by simply counting and returning the correct number of rows from the root of the plan.&amp;nbsp; Although this strategy might work for a really trivial update plan such as the one above, it would not work for more complex update plans.&amp;nbsp; For instance, if we add a unique index to our table, the update plan becomes substantially more complex:&lt;/P&gt;
&lt;P&gt;CREATE UNIQUE INDEX TA ON T(A)&lt;BR&gt;UPDATE T SET A = A + 1&lt;/P&gt;
&lt;P mce_keep="true"&gt;Rows&amp;nbsp; Executes&lt;BR&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UPDATE [T] set [A] = [A]+@1&lt;BR&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; |--Index Update(OBJECT:([T].[TA]), SET:([Bmk10061024] = [Bmk1006],[A1025] = [T].[A]))&lt;BR&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Collapse(GROUP BY:([T].[A]))&lt;BR&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Filter(WHERE:(NOT [Expr1021]))&lt;BR&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Sort(ORDER BY:([T].[A] ASC, [Act1023] ASC))&lt;BR&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Split&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Update(OBJECT:([T]), SET:([T].[A] = [Expr1004]))&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Compute Scalar(DEFINE:([Expr1021]=[Expr1021]))&lt;BR&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Compute Scalar(DEFINE:([Expr1021]=CASE WHEN [Expr1005] THEN (1) ELSE (0) END))&lt;BR&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Compute Scalar(DEFINE:([Expr1004]=[T].[A]+(1), [Expr1005]=CASE WHEN [T].[A] = ([T].[A]+(1)) THEN (1) ELSE (0) END))&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Top(ROWCOUNT est 1)&lt;BR&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Scan(OBJECT:([T]))&lt;/P&gt;
&lt;P&gt;I'm not going to try in this post to explain all of the details of the above plan.&amp;nbsp; I'll save that for a future post.&amp;nbsp; However, observe that in updating 1 row, the root of this plan returns 2 rows.&amp;nbsp; Counting 1 row from the root of this plan would not achieve an accurate result. &amp;nbsp;By placing the ROWCOUNT top above the table scan, the optimizer can ensure that the server updates exactly the correct number of rows regardless of the complexity of the remainder of the plan.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4049360" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/craigfr/archive/tags/Updates/default.aspx">Updates</category><category domain="http://blogs.msdn.com/craigfr/archive/tags/Top/default.aspx">Top</category></item></channel></rss>