Română (România)English (United Kingdom)French (Fr)
JUser::_load: Unable to load user with id: 98
Wednesday, 07 April 2010 10:56

MSSQLs’ Version of MySQLs’ LIMIT

Rate this item
(11 votes)

One of MySQLs’ things I like, besides being free, is the LIMIT clause. The syntax is simple – SELECT * FROM Table A WHERE 1=1 LIMIT OFFSET, ROW_COUNT, where offset is the offset of the first row to return and row_count is the number of rows to return. This clause allows web programmers to paginate a large set of records from a database.

Pagination is useful when you are dealing with huge numbers of records. Let’s take for example the case of a Bookstore that decides to open virtual store. It has more than 100,000 books for online sale. If a customer visits the website and loads the books page, he will have to wait a while until all of the records are displayed and after that he will have to scroll through thousands of records to find a book. Now let’s multiply the number of online customers by 1,000, all accessing the books page simultaneously. About 100 million records will be queried from the database – imagine the overhead. This is where the magic of pagination comes in – if you decide to display only 50 records per page and let the customer go to the next 50 records (by accessing the next page) you will increase the readability and reduce the loading time of the page and reduce the overhead. In this case only 50,000 records will be queried, that’s half the number of records of the first query per customer.

Now that we are familiar with the benefit of pagination it’s time to present how to accomplish it using a Microsoft SQL Server Database.

Because of the fact that MSSQL does not have a LIMIT clause some programmers might attempt to use huge SELECT constructions using TOP and WHERE. Even if these elaborate constructions may do the work, they are time and resource consuming. The option that should be used is the ROW_NUMBER() OVER (ORDER BY field) syntax. If we put this syntax in an ordinary select it will add an additional column that will hold the row number of the record. There is only one last simple step until we reach the LIMIT equivalent. Another select syntax is needed where you condition the row number column. The final query will look like this: SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY field) AS Row, * FROM Table WHERE 1=1) A WHERE Row BETWEEN OFFSET AND OFFSET+ROW_COUNT. This query will return the same records as the top query used in MySQL.

Let’s illustrate the above in a simple example using the Bookstore that decides to go online.

I will create a simple table that holds the records regarding the books for sale:

CREATE TABLE [dbo].[Books](

[Author] [varchar](255) NOT NULL,

[Book] [varchar](255) NOT NULL,

[PublishingHouse] [varchar](255) NOT NULL,

[Price] [decimal](8, 2) NOT NULL

)

After populating the table with some records the table content is:

Author Book PublishingHouse Price
Jules Verne 20,000 Leagues Under the Sea Adevarul Holding 10.99
Jules Verne Dick Sand, A Captain at Fifteen Adevarul Holding 10.99
Jules Verne A Journey to the Centre of the Earth Adevarul Holding 10.99
Dennis Lehane Shutter Island Paralela 45 35.00
Marcel Ayme Eloiza Aramis 15.00
Jim Nisbet Codex siracuza Nemira 10.00
Jesus Pardo Marcus ulpius traianus Artemis 9.80

Now let’s test the ROW_NUMBER() OVER functionality:

SELECT

*

FROM (SELECT ROW_NUMBER() OVER (ORDER BY Author) AS Row, * FROM Books) A WHERE

Row BETWEEN 3 AND 6

ORDER BY Row

The result is:

Row Author Book PublishingHouse Price
3 Jim Nisbet Codex siracuza Nemira 10.00
4 Jules Verne 20,000 Leagues Under the Sea Adevarul Holding 10.99
5 Jules Verne Dick Sand, A Captain at Fifteen Adevarul Holding 10.99
6 Jules Verne A Journey to the Centre of the Earth Adevarul Holding 10.99

Another functionality that you can play around with is RANK() OVER, which mostly does the same thing with the exception that if you do not provide a uniquely record identifier in the OVER brackets it will duplicate some records in the Row column.

If we query for:

SELECT

*

FROM (SELECT RANK() OVER (ORDER BY Author) AS Row, * FROM Books) A WHERE

Row BETWEEN 3 AND 6

ORDER BY Row

The result will be:

Row Author Book PublishingHouse Price
3 Jim Nisbet Codex siracuza Nemira 10.00
4 Jules Verne 20,000 Leagues Under the Sea Adevarul Holding 10.99
4 Jules Verne Dick Sand, A Captain at Fifteen Adevarul Holding 10.99
4 Jules Verne A Journey to the Centre of the Earth Adevarul Holding 10.99


This is somehow acceptable if we consider the sense of word rank. If we want the same result as in the ROW_NUMBER case we will have to query for:

SELECT

*

FROM (SELECT RANK() OVER (ORDER BY Author, Book) AS Row, * FROM Books) A WHERE

Row BETWEEN 3 AND 6

ORDER BY Row

And the result will surely be:

Row Author Book PublishingHouse Price
3 Jim Nisbet Codex siracuza Nemira 10.00
4 Jules Verne 20,000 Leagues Under the Sea Adevarul Holding 10.99
5 Jules Verne Dick Sand, A Captain at Fifteen Adevarul Holding 10.99
6 Jules Verne A Journey to the Centre of the Earth Adevarul Holding 10.99

 

I invite you to explore these useful functionalities of Microsoft SQL Server 2005 and also would like to give a special thanks to my colleague, Cristian Bardas, for discovering them and sharing the knowledge.


Last modified on Thursday, 08 April 2010 10:50

E-mail: This e-mail address is being protected from spambots. You need JavaScript enabled to view it

Image Gallery

{gallery}156{/gallery}

Add comment