sqlsql-serverpaginationsql-server-2012

Get total row count while paging


I have a search screen where the user has 5 filters to search on.
I constructed a dynamic query, based on these filter values, and page 10 results at a time.
This is working fine in SQL2012 using OFFSET and FETCH, but I'm using two queries to do this.

I want to show the 10 results and display the total number of rows found by the query (let's say 1000).
Currently I do this by running the query twice - once for the Total count, then again to page the 10 rows.
Is there a more efficient way to do this?


Solution

  • You don't have to run the query twice.

    SELECT ..., total_count = COUNT(*) OVER()
    FROM ...
    ORDER BY ...
    OFFSET 120 ROWS
    FETCH NEXT 10 ROWS ONLY;
    

    Based on the chat, it seems your problem is a little more complex - you are applying DISTINCT to the result in addition to paging. This can make it complex to determine exactly what the COUNT() should look like and where it should go. Here is one way (I just want to demonstrate this rather than try to incorporate the technique into your much more complex query from chat):

    USE tempdb;
    GO
    CREATE TABLE dbo.PagingSample(id INT,name SYSNAME);
    
    -- insert 20 rows, 10 x 2 duplicates
    INSERT dbo.PagingSample SELECT TOP (10) [object_id], name FROM sys.all_columns;
    INSERT dbo.PagingSample SELECT TOP (10) [object_id], name FROM sys.all_columns;
    
    SELECT COUNT(*) FROM dbo.PagingSample; -- 20
    
    SELECT COUNT(*) FROM (SELECT DISTINCT id, name FROM dbo.PagingSample) AS x; -- 10
    
    SELECT DISTINCT id, name FROM dbo.PagingSample; -- 10 rows
    
    SELECT DISTINCT id, name, COUNT(*) OVER() -- 20 (DISTINCT is not computed yet)
     FROM dbo.PagingSample
     ORDER BY id, name
     OFFSET (0) ROWS FETCH NEXT (5) ROWS ONLY; -- 5 rows
    
    -- this returns 5 rows but shows the pre- and post-distinct counts:
    SELECT PostDistinctCount = COUNT(*) OVER() -- 10, 
      PreDistinctCount -- 20, 
      id, name 
    FROM 
    (
      SELECT DISTINCT id, name, PreDistinctCount = COUNT(*) OVER() 
        FROM dbo.PagingSample
        -- INNER JOIN ...
    ) AS x
    ORDER BY id, name
    OFFSET (0) ROWS FETCH NEXT (5) ROWS ONLY;
    

    Clean up:

    DROP TABLE dbo.PagingSample;
    GO