It is known that ORDER BY clause has been improved in 'Denali' (code name of the upcoming version SQL Server 2012) adding more options as a result from looking for new solutions to work on specific business cases such as fetching top N rows per window of results from the result set which we used to do by combining TOP, OVER, and ORDER BY. Nevertheless, the performance of this approach was poor and not even close to being as good as
OFFSET and FETCH in 'Denali'.
OFFSET keyword is used for excluding first N rows (default value is zero) whereas FETCH is used for reading/fetching M rows per each window of results starting from position (N+1). Therefore, OFFSET and FETCH allow to get only rows from position (N+1) to (N+M). I do recommend creating one Index (preferably Index Clustered) on ORDER BY columns and using these columns on WHERE clause as much as possible in order to avoid costly operations such as Index Scans or Table Scans. I have performed many tests on 10 millions of rows, and to be to honest, it worked very well. To illustrate, the following query will start fetching from position 4+1=5 (OFFSET position=4 for SQL Server) to the end of the table.
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address
FROM dbo.Customers
ORDER BY CustomerID
OFFSET (4) ROWS
Now with DESC the results will be different (excluding last 4 rows)
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address
FROM dbo.Customers
ORDER BY CustomerID DESC
OFFSET (4) ROWS
Basically, each execution of the query is done independently, which means that not only are the results sent back to the client side, but also immediately the resources are released. Here is another example how to get only the top 3 rows excluding first 4 rows (OFFSET=4), which means that it only reads the fifth, sixth, and seventh rows.
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address --/..*.../
FROM dbo.Customers
ORDER BY CustomerID
OFFSET (4) ROWS
FETCH NEXT 3 ROWS ONLY -- FETCH FIRST 3 ROWS ONLY
GO
Having a need to read the next three rows (eighth, ninth, and tenth rows), we have to increase OFFSET value in three.
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address --/..*.../
FROM dbo.Customers
ORDER BY CustomerID
OFFSET (7) ROWS ---ó OFFSET (7) ROW
FETCH NEXT 3 ROWS ONLY -- ó FETCH FIRST 3 ROWS ONLY
GO
Here the script to read dynamically each result by using variables (and optionally
OPTIMIZE FOR hint to optimise this Ad-Hoc query).
DECLARE @Start INT, @Next INT
SET @Start=0
SET @Next=3
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address --/..*.../
FROM dbo.Customers
ORDER BY CustomerID
OFFSET (@Start) ROWS
FETCH NEXT @Next ROWS ONLY
OPTION(OPTIMIZE FOR (@Start=4,@Next=5))
GO
Now the same code inside an stored procedure.
CREATE PROCEDURE dbo.PageFetch(@PageNumber INT, @PageSize INT)
AS
BEGIN
SELECT ROW_NUMBER() OVER (ORDER BY CustomerID) AS 'Nro',
CustomerID, CompanyName, ContactName, ContactTitle, Address --/..*.../
FROM dbo.Customers
ORDER BY CustomerID
OFFSET (@PageSize * (@PageNumber -1) ) ROWS
FETCH NEXT @PageSize ROWS ONLY
END
Querying the results for second, third and fifth windows of results with OFFSET.
EXEC dbo.PageFetch 2,3;
GO
EXEC dbo.PageFetch 3,3;
GO
EXEC dbo.PageFetch 5,3;
Luckily, OFFSET and FETCH are supported by subqueries, functions, derived tables, but not by indexed views and using directly with TOP,
OVER, INSERT, UPDATE, MERGE, o DELETE (except inside of independent queries at lower level such as subqueries). Here an illustration.
SELECT TOP(2) ROW_NUMBER() OVER(ORDER BY CustomerID) AS 'NRO', CompanyName, ContactName, ContactTitle FROM (
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address --/..*.../
FROM dbo.Customers
ORDER BY CustomerID
OFFSET (4) ROWS ---ó OFFSET (4) ROW
FETCH NEXT 3 ROWS ONLY -- ó FETCH FIRST 3 ROWS ONLY
) AS T1
Finally, using it with Views.
CREATE VIEW dbo.v1
as
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address --/..*.../
FROM dbo.Customers
ORDER BY CustomerID
OFFSET (0) ROWS ---ó OFFSET (4) ROW
GO
SELECT CustomerID, CompanyName, ContactName FROM dbo.v1
WHERE CustomerID LIKE 'AN%'
To sum up, OFFSET and FETCH are excellent improvements which allow to implement solutions to fetch only specific pages/windows of results from the result set with ease. Let me know any remarks you may have. Thanks for reading!