Arctus Blog: Advanced SQL: Auto-numbering rows for fast paging (SQL Server 2005) http://blog.arctus.co.uk/articles/2007/04/19/advanced-sql-auto-numbering-rows-for-fast-paging-sql-server-2005 en-us 40 Advanced SQL: Auto-numbering rows for fast paging (SQL Server 2005) <p>I have always been an advocate of performing as much data-shaping as possible inside my stored procedures. Stored procedures are far better (and significantly faster) at working with data than .NET can ever be.</p> <p>For serious applications, this meant handling &#8216;paging&#8217; inside the procedure. Typically, my procedures would take a page number as a paramter and, using an in-memory TABLE variable with an IDENTITY column, return just the results for the current page.</p> <p>With SQL Server 2005 there is now a better way. The system function <em>rank()</em> can work out a row number for our results on the fly. As always, examples are for NorthWind:</p> <pre><code>SELECT *, rank() OVER (ORDER BY ProductName) FROM Products;</code></pre> <p>If you run the query, you will see that the last column is an auto-incrementing field.</p> <p>That looks great - absolutely perfect for paging, and some of you even probably tried doing something like this already:</p> <pre><code>SELECT *, rank() OVER (ORDER BY ProductName) AS rank FROM Products WHERE rank BETWEEN 11 AND 20</code></pre> <p>Yikes. It looks good, but sadly the result of rank() isn&#8217;t available to the WHERE clause (because rank runs after the WHERE to number only the results that actually match our criteria).</p> <p>We can get around this by ranking our results in a sub-query. An outer query can restrict the rows based on the value produced by rank. This means that our sub-query must contain the main where clause for any search criteria we might have (because only the successful matches will be ranked), and the OVER (ORDER BY &#8230; must be the order we wish our results to be returned in. Here&#8217;s the full query (written to use WITH instead of a regular sub-query):</p> <pre><code>WITH ProductRowNumber (ProductId, RowNum) AS ( SELECT ProductId, rank() OVER (ORDER BY ProductName) AS RowNum FROM Products ) SELECT * FROM Products p INNER JOIN ProductRowNumber pn ON pn.ProductId = p.ProductId WHERE pn.RowNum BETWEEN 11 AND 20 ORDER BY pn.RowNum</code></pre> <p>You would need to work out the values passed to BETWEEN based on the current page number (and size), but other than that it&#8217;s ready to go.</p> Thu, 19 Apr 2007 08:02:00 -0400 urn:uuid:4bc5cde2-10ae-4c04-a0d3-814a46c8b0e9 adrian@arctus.co.uk (Adrian O'Connor) http://blog.arctus.co.uk/articles/2007/04/19/advanced-sql-auto-numbering-rows-for-fast-paging-sql-server-2005 SQL Server