SingingEels : Development Community & Resource

Login

Articles

  • ADO.NET (2)
  • ASP.NET (36)
  • LINQ (5)
  • Security (2)
  • Silverlight (3)
  • SQL (7)
  • Standards (5)
  • WCF (2)

Syndication

  • Articles RSS
  • Blogs RSS

Contribute

  • Our Authors List
  • Member Sign-Up
  • Suggestions Box

Pagination In SQL Server 2005

(May 26 2007 - 05:43:25 PM by Timothy Khouri) - [print article]

Pagination In SQL

If you've used other flavors of SQL (such as MySQL or PostgreSQL) and now you have switched over to TSQL (Microsoft SQL Server) you may have noticed that there is no native way of doing pagination. In some SQL versions, pagination was as easy as "SELECT * FROM 'myTable' LIMIT 0, 10" and that would pull out the first 10 records from your table. SQL Server 2005 introduced a beautiful new world of 'ranking functions', one of which we can use to easily perform pagination directly in SQL.

While these other flavors of SQL implimented pagination only for the sake of pagination - meaning the absolutely only reason for the functions is to limit results sets into 'pages' of data - SQL Server 2005's ranking functions provide a lot of power and information. However, for this article, we are only going to demonstrate how to use these abilities for the sake of pagination.

RowNumber In SQL

First of all, we'll need to get the "row number" from a result set. This is the basis of pagination because you need to know what row you are on to begin demanding only a subset from SQL. First we'll do just a basic query and look at the results:

SELECT
   *
FROM
   dbo.Customers
ORDER BY
   LastName, FirstName

This query above will display the following results:

SQL results for 'customer' table without pagination

Now we'll make a slight modification to add a "RowNumber" field.

SELECT
   ROW_NUMBER() OVER (ORDER BY LastName, FirstName) AS RowNumber,
   *
FROM
   dbo.Customers

This syntax may look a little odd, but it's not that difficult. First of all, the ROW_NUMBER() function (just like all other 'ranking functions' in SQL SERVER 2005) needs an OVER clause. Notice that we moved the ORDER BY clause inside the OVER clause. Basically what this is doing is telling SQL how we want the ROW_NUMBER() function to number each record. So we are telling SQL to number the records 1, 2, 3, 4 in the order of LastName then FirstName. Here are the results:

SQL results for 'customer' table with pagination

So now that we have a "RowNumber" field, we can add a simple WHERE clause to get only a limited number of records. We will have to wrap query in another SELECT statment so that we can use all of our dynamic columns (such as our RowNumber column) by their names that we assigned them. Also, I'm going to use the TOP clause to limit the query to only return 2 records at a time. Lets see the query, and the results:

SELECT TOP 2 * FROM (SELECT
   ROW_NUMBER() OVER (ORDER BY LastName, FirstName) AS RowNumber,
   *
FROM
   dbo.Customers) _myResults
WHERE
   RowNumber > 2

Notice I 'aliased' the first query and called it "_myResults". This basically treats that inner query as a VIEW so that I could pull out that first column by it's name, RowNumber. Here are the results:

Paginated SQL results for 'customer' table

Conclusion

Though it may seem like you have to do a lot more work in order to get pagination in SQL Server 2005, the pay-off is that not only does the code make sense, but you gain a lot more power with the ranking functions TSQL. Try playing around with the parameters to see how they affect your queries and what you can do with them.

  • Jul 17 2007 - 08:36:30 AM nmg196

    It's a bit of a weird syntax (and a waste of system resources) to use TOP in this case. Why not just get the correct rows to start with by doing:

    WHERE RowNumber >= 10 AND RowNumber <=20

    or even:

    WHERE RowNumber BETWEEN 10 and 20

    I also think it looks neater if you use WITH:

    WITH OrderedResults AS

    (SELECT EntryName, ROW_NUMBER() OVER (order by EntryName) as RowNumber FROM tblEntry)

    SELECT *

    FROM OrderedResults

    WHERE RowNumber between 10 and 20

  • Jul 17 2007 - 03:00:55 PM Timothy Khouri

    Well, it may seem like weird syntax to use "TOP" but it's not realy. Think about it... if your page size is 30, you'll always be selecting TOP 30, whereas if you were doing it the "BETWEEN" way, your query could say "WHERE RowNumber BETWEEN 70 AND 100".

    It's personaly preference as the performance gain / loss is negligible.

    As for using a CTE, sure you can do that if you'd like. I personally would myself, but I didn't want to confuse the article's focus on pagination. There is another article writen about CTE's and more complex SQL.

    As an FYI: LINQ To SQL will use the "TOP X" syntax if you do a ".Take(10)", but if you do a ".Skip(5).Take(10)" then it will use the between.

  • Jul 18 2007 - 02:56:09 PM Josh Stodola

    >> the pay-off is that not only does the code make sense <<

    IMHO the LIMIT keyword makes a helluva lot more sense than this does.

  • Jul 18 2007 - 07:05:24 PM Timothy Khouri

    Well, I'll give you the fact that it's a lot easier to use the LIMIT keyword (in other flavors of SQL), but it doesn't make more sense. Actually, it quite the opposite to have a "pagination-specific" function in SQL.

    That makes as much sense as having a SQL function that converts a TEXT field in your database to an HTML document. Would it save you work (if you needed such a thing), yes; would it make sense to have such specific functionality in SQL, not really.

    But I do agree that LIMIT was very easy to use back in my PHP days. But with Visual Studio 2008 (and more specifically LINQ), pagination is easier than ever.

  • Apr 16 2008 - 04:24:05 AM khaz

    We have implemented this on our ASP.net application, however it seems when you use a where clause within the CTE, the query suffers from performance issues. The query we apply the where clause on runs within 5 seconds, admittedly, we have over 200,000 records but surely the CTE query will only bring back a subset of the dataset especially when they have been filtered even more.

    If we leave the where clause on the outside of the contruction of the CTE and add it into the select statement, the subset of records may not display anything as there are no records on that page for the filter option selected.

    Any ideas???

  • Nov 27 2008 - 05:48:22 AM sondlerd

    Great Post, thanks for the info. It worked for a more complicated query containing a few joins as well. however, you must make sure the JOINS and WHERE clause are inside the first query (noted by the ...)AS MyResults) and add the WHERE clause for RowNumber is in the outter select statement. For example (note: these fields aren't from Northwind, it's just an example):

    SELECT TOP 2 * FROM (SELECT

    ROW_NUMBER() OVER (ORDER BY LastName, FirstName) AS RowNumber, *

    FROM

    dbo.Customers INNER JOIN dbo.Orders on Customers.ID = Orders.CustomerID

    WHERE

    Orders.OrderDate > 11\01\2008) AS MyResults

    WHERE RowNumber > 2

    Cheers,

    Rob

You must be logged in to add comments. If you have not already done so, you can create an account here. If you already are a member, you first need to login before you can comment.

Developer / Architect / Author

People to Follow

Experts in the categories related to this article.

There are no experts related to this article.

Related Blogs

These are the most recent blog posts related to this article.

There are no blog posts related to this article.

Related Ads

SingingEels.com as of Jul 03 2009 - 05:01:15 PM - (0.2344125)