Paging became quite simpler & easy to script and manage by using OFFSET & FETCH NEXT keywords in SQL Server 2012 & above. I have written quite a detailed article earlier about it and implemented it in my most of the solutions wherever required. However, when you implement/use paging in your script, you face a big challenge, that is, to find the total number of records in that particular result-set.
Given below are the three methods which you can use to get the total row count from OFFSET / FETCH NEXT.
Before proceeding with the solutions, let me create a sample.
Sample :
USE AdventureWorks2014 GO -- Create Sample Table Table CREATE TABLE [dbo].[SalesOrderDetail]( [SalesOrderID] [int] NOT NULL, [SalesOrderDetailID] [int] NOT NULL, [CarrierTrackingNumber] [nvarchar](25) NULL, [OrderQty] [smallint] NOT NULL, [ProductID] [int] NOT NULL, [SpecialOfferID] [int] NOT NULL, [UnitPrice] [money] NOT NULL, [UnitPriceDiscount] [money] NOT NULL, [LineTotal] [numeric](38, 6) NOT NULL, [rowguid] [uniqueidentifier] NOT NULL, [ModifiedDate] [datetime] NOT NULL ) ON [PRIMARY] GO -- Insert bulk data into sample table -- It may take few minutes depends upon the server performance INSERT INTO [dbo].[SalesOrderDetail] SELECT * FROM [SALES].[SalesOrderDetail] GO 100 -- Verfiy the data Select * from [dbo].[SalesOrderDetail] GO
Method 1 : Using COUNT(*) OVER()
USE AdventureWorks2014 GO DECLARE @PageSize INT = 10, @PageNum INT = 1; SELECT [SalesOrderID] , [SalesOrderDetailID] , [CarrierTrackingNumber] , [OrderQty] , [ProductID] , [SpecialOfferID] , [TotalCount]= COUNT(*) OVER() FROM [dbo].[SalesOrderDetail] ORDER BY SalesOrderID OFFSET (@PageNum-1)*@PageSize ROWS FETCH NEXT @PageSize ROWS ONLY; GO --OUTPUT
Method 2 : Using Common Table Expression
USE AdventureWorks2014 GO DECLARE @PageSize INT = 10, @PageNum INT = 1; ;WITH Main_CTE AS( SELECT [SalesOrderID] , [SalesOrderDetailID] , [CarrierTrackingNumber] , [OrderQty] , [ProductID] , [SpecialOfferID] FROM [dbo].[SalesOrderDetail] ) , Count_CTE AS ( SELECT COUNT(*) AS [TotalCount] FROM Main_CTE ) SELECT * FROM Main_CTE, Count_CTE ORDER BY Main_CTE.SalesOrderID OFFSET (@PageNum-1)*@PageSize ROWS FETCH NEXT @PageSize ROWS ONLY GO --OUTPUT
Method 3 : Using Cross Apply
USE AdventureWorks2014 GO DECLARE @PageSize INT = 10, @PageNum INT = 1; SELECT [SalesOrderID] , [SalesOrderDetailID] , [CarrierTrackingNumber] , [OrderQty] , [ProductID] , [SpecialOfferID] , [TotalCount] FROM [dbo].[SalesOrderDetail] CROSS APPLY (SELECT COUNT(*) TotalCount FROM [dbo].[SalesOrderDetail] ) [Count] ORDER BY SalesOrderID OFFSET (@PageNum-1)*@PageSize ROWS FETCH NEXT @PageSize ROWS ONLY GO --OUTPUT
All of the above methods give you the same result-sets. Lets view their performance given below.
S.No |
Method |
CPU Time |
Elapsed Time |
1 |
COUNT(*) OVER() |
30654 ms |
40372 ms |
2 |
Common Table Expression |
11762 ms |
7665 ms |
3 |
Cross Apply |
11794 ms |
7373 ms |
Conclusion :
On the basis of above results, I would recommend that you either use Common Table Expression or Cross Apply to get the faster results.
Note : The above queries have been tested on ~12 Million records.