Interview Question of the Week #025 – How to Implement Paging in SQL Server?

Paging is a good live-coding interview question because the row boundaries expose small mistakes. My original example used SQL Server 2005 and ROW_NUMBER(); that is still the right context for this answer.

A sliding viewfinder reveals one frame in a longer strip of pictures

Question: How do you implement paging in SQL Server?

Answer: Give the rows a stable order, number them, and filter for the requested range. In the original AdventureWorks schema, ContactID breaks name ties so the order is deterministic. This derived-table version selects 20 rows, numbered 121 through 140:

USE AdventureWorks2025;
GO
DECLARE @StartRow int, @EndRow int;
SET @StartRow = 120;
SET @EndRow = 140;

SELECT FirstName, LastName, EmailAddress
FROM
(
    SELECT PC.FirstName, PC.LastName, EA.EmailAddress,
           ROW_NUMBER() OVER
           (ORDER BY PC.FirstName, PC.LastName, PC.BusinessEntityID, EA.EmailAddress) AS RowNumber
    FROM Person.Person AS PC
    INNER JOIN Person.EmailAddress AS EA
        ON EA.BusinessEntityID = PC.BusinessEntityID
) AS PersonContact
WHERE RowNumber > @StartRow
  AND RowNumber <= @EndRow
ORDER BY RowNumber;

My old post also showed the same method with a CTE. Here is that form with the same corrected boundary:

USE AdventureWorks2025;
GO
DECLARE @StartRow int, @EndRow int;
SET @StartRow = 120;
SET @EndRow = 140;
WITH PersonContact AS
(
    SELECT PC.FirstName, PC.LastName, EA.EmailAddress,
           ROW_NUMBER() OVER
           (ORDER BY PC.FirstName, PC.LastName, PC.BusinessEntityID, EA.EmailAddress) AS RowNumber
    FROM Person.Person AS PC
    INNER JOIN Person.EmailAddress AS EA
        ON EA.BusinessEntityID = PC.BusinessEntityID
)
SELECT FirstName, LastName, EmailAddress
FROM PersonContact
WHERE RowNumber > @StartRow
  AND RowNumber <= @EndRow
ORDER BY RowNumber;
All twenty rows from the current stable-order paging query, rows 121 through 140
All twenty rows of the current page, numbered 121 through 140 by the query above. The separately executed CTE and derived-table forms returned the same ordered rows.

The original code used RowNumber < @EndRow, which returned only rows 121 through 139. The inclusive upper bound fixes that off-by-one error. On SQL Server 2012 and newer, OFFSET/FETCH is another way to request a page, but it needs the same deliberate ordering. If rows change between page requests, even a unique sort key cannot freeze the underlying data.

For comparisons, see SQL 2000 versus SQL 2005 paging and CTE versus derived-table paging.

Want to try this yourself? Here is how to install AdventureWorks.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

SQL Scripts
Previous Post
Interview Question of the Week #024 – What is the Best Recovery Model?
Next Post
Interview Question of the Week #026 – 64 Bit Vs 32 Bit Confusion

Related Posts

1 Comment. Leave new

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.