views:

68

answers:

3

When creating a record "grid" with custom paging what is the best/optimal way to query the total number of records as well as the records start-end using C#?

SQL to return paged record set:

SELECT Some, Columns, Here FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY Column ASC) AS RowId, *
    FROM
        Records
    WHERE
        (...)
) AS tbl
WHERE ((RowId > @Offset) AND (RowId <= (@Offset + @PageSize)) )

SQL to count total number of records:

SELECT COUNT(*) FROM Records WHERE (...)

Right now, I make two trips to the server: one for getting the records, and the other for counting the total number of records.

What is/are the best way(s) to combine these queries to avoid multiple DB trips?

A: 

One way would be to turn your query into a stored procedure, and then have an output parameter that allows you to count the total records. You would populate the output parameter inside the procedure, then return your recordset the same way you do now.

jaltiere
+2  A: 

You can use stored procedure with output parameter (or return value) to pass total number of rows.

create procedure dbo.Stuff_GetAll (
    @StartRowIndex int, -- zero based
    @MaximumRows int
)
as
begin
    declare @TotalRows int

    select @TotalRows = count(*) 
    ...

    if (@TotalRows > 0 and @MaximumRows > 0)
    begin
        ;with T as (
            select *, row_number() over ()
            ...
        )
        select T.* from T
        where Row between @StartRowIndex + 1 and (@StartRowIndex + @MaximumRows)
    end

    return @TotalRows
end
GO

You may want to add a check to query count() only when a first page is requested (can be quite expensive).

UserControl
Personally, I hate using SPs. They have always been a nightmare for me to maintain. Is there anyway to do this without creating a SP?
David Murdoch
Yes, you can use union to combine both queries into a single one but that will require dynamic query construction (looks too complex for me even if i hated SPs :) .
UserControl
I was thinking of using NextResult after "SELECT"ing two tables. [like here](http://www.dotnetjohn.com/articles.aspx?articleid=23). But I don't know how to implement this in C#.
David Murdoch
Interesting, never heard of possibility to separate multiple SELECTs with ; in a single query. Here is a pseudo code (cannot test it at the moment, sorry): string query = "select pageddata ... ; select count(*) ..." var command = new SqlCommand(query, conn); IDataReader reader = command.ExecuteReader(); while (reader.Read()) // process row reader.NextResult(); // proceed to the count() result if (reader.Read()) long totalRows = (long)reader[0];
UserControl
In my experience, this solution can perform poorly as you have to execute the same query twice. I.e., in order to populate @TotalRows, you have execute the raw query (e.g. From clause, Where, cause etc) twice (once for Count(*) and once in the CTE where you get the paged results). If the query is very complicated and/or if you have a lot of data, this will not perform well.
Thomas
A: 

The fastest method I have found is to return the count of rows in the resultset:

With PagedItems As
    (
    Select ...
        , ROW_NUMBER() OVER ( ORDER BY Column ASC ) As Seq
        , ROW_NUMBER() OVER ( ORDER BY Column DESC ) As ReverseSeq
    From Table
    Where ....
    )
Select ..., ( ReverseSeq + Seq - 1) As TotalRows
From PagedItems
Where RowId > @Offeset
    ANd RowId <= ( @Offset + @PageSize )
Thomas
It's not. While looking very elegant, even if you have an index which is used for ASC ordering, in practice DESC case is much slower. Moreover, the result set needs to carry the same information in every row returned.
UserControl
@UserControl - Don't think so. The system will make a single pass through the rows. In my tests it was noticeably faster than a temp table or a subquery with count(*).
Thomas
@UserControl - In addition, the return of the count as a column for every row return is insignificant. Remember we are only getting back at most the @PageSize in rows so an additional 4 bytes for each row will have no impact on performance.
Thomas
@UserControl - http://www.sqlservercentral.com/articles/T-SQL/66030/
Thomas
i'd bet that SQL Server 2005 can guarantee the same speed for both ASC and DESC ordering queries (especially when you have a single index on the column). In reality you often don't even have an index on the columns you want ordering on (free user-selectable column ordering in a grid, for example). In fact, ordering has nothing to do with the topic except that the ranking function requires it (but not both ASC and DESC at the same time!) to make sure we have consistent result sets.
UserControl