views:

83

answers:

2

Hello everyone,

I am using SQL Server 2008 Enterprise on Windows Server 2008 Enterprise. I am using the following code to return a part of data for a query to implement paging (i.e. page up/down to show a part of result at each page, like Google search result paging) on my web application (I use pageCount as number of results showed on each page, and startPos as the start number of result). For example, pageCount 10 means show 10 results for each page, startPos = 0 means the first page, startPos = 1 means the 2nd page, etc.

My question is how to get the total number of results efficiently in my scenario? My major concern is how to implement paging (i.e. touch only a part of result) and at the same time retrieve the total number of results?

SELECT *
 FROM   (SELECT
    t.foo, t.goo, ROW_NUMBER() OVER (order by t.zoo DESC ) AS rowNum
   FROM
    dbo.mycorp  t

   WHERE
    (t.foo LIKE '%'+@search+'%'
    or t.foo  LIKE '%'+@search+'%'
    )
    ) tt
    WHERE  tt.rowNum between @startPos and  @pageCount + @startPos-1

thanks in advance, George

+3  A: 

This will mean a redundant column, with the same value for every row:

WITH cte AS (
    SELECT t.foo, 
           t.goo, 
           ROW_NUMBER() OVER (ORDER BY t.zoo DESC ) AS rowNum,
           (SELECT COUNT(*)
              FROM dbo.mycorp
             WHERE t.foo LIKE '%'+@search+'%'
                OR t.foo  LIKE '%'+@search+'%') AS total_count
      FROM dbo.mycorp  t
     WHERE t.foo LIKE '%'+@search+'%'
        OR t.foo  LIKE '%'+@search+'%')
SELECT c.foo,
       c.goo,
       c.total_count
  FROM cte c
 WHERE c.rowNum BETWEEN @startPos 
                    AND @pageCount + @startPos-1

A more efficient means of searching text is to use SQL Server's Full Text Search (FTS) functionality.

OMG Ponies
+1 one point though - dont u think you need to add the where clause to the total_count query as well?
InSane
@In Sane: Wasn't going to include it, but then decided to mention Full Text Search... updated.
OMG Ponies
Thanks OMG Ponies, I noticed you add count in your code, I am not sure whether it will impact performance since I think count(*) will touch all results in order to calculate the total result number, but I only need a part of result to implement paging (so, my original post will only touch a part of result). I am concerning your solution will impact performance compared to my previous one since it touch all results. Any comments? Please feel free to correct me if I am wrong.
George2
@George2: I don't understand what are you talking about--I added the WHERE filteration to the COUNT portion over 40 minutes ago.
OMG Ponies
@OMG Ponies, I mean you need to touch all matched results in order to get the correct count, correct? But my original implementation on need to touch a page (10 in default) of result. Suppose there are 1000 matched results in my query condition, you need to touch 1000, but I only to touch 10. Will your solution impact performance?
George2
@George2: If that's all you need, get the count of the rows returned from the resultset.
OMG Ponies
Thanks @OMG Ponies, I am wondering how good performance is select count(*) -- from -- where statement? Does it need to touch all matched results in order to return matched results' count? If so, the performance of select count(*) -- from -- where is similar to select somerow -- from -- where?
George2
@OMG Ponies, why using full text mining could resolve my issue of get the total number of matched query results and at the same time implementing paging function?
George2
@George2: Full Text Search (FTS) functionality could return more valid results, but primarily FTS is more efficient than the approach you provided in your question.
OMG Ponies
Thanks, question answered!
George2
+1  A: 

I would think about using the multiple recordsets if your data access technology supports it (I know ADO.NET does support this).

Include the following after your query

SELECT COUNT(*) AS TotalRecordCount
 FROM   dbo.mycorp  t 
   WHERE 
    (t.foo LIKE '%'+@search+'%' 
    or t.foo  LIKE '%'+@search+'%' 
    ) 
    ) tt

In your data access layer, you would need to switch to the next recordset after processing the search results in order to retrieve the total count. Using ADO.NET, this would require calling dataReader.NextResult().

The most important thing is to measure performance. I have found that in some cases, using a separate select statement rather than a sub-query can result in faster performance as the Query optimizer is able to better optimize the query. Either way, you will need suitable indexing on the search column.

Teevus
How does indexing a column while using % as the opening character in a like statement help a column become sargable?
Thanks Teevus, I noticed you add count in your code, I am not sure whether it will impact performance since I think count(*) will touch all results in order to calculate the total result number, but I only need a part of result to implement paging (so, my original post will only touch a part of result). I am concerning your solution will impact performance compared to my previous one since it touch all results. Any comments? Please feel free to correct me if I am wrong.
George2
@Teevus, you mentioned "Include the following after your query", do you mean add another select or a sub-query? In order to reduce communication misunderstanding, could you post all the code please?
George2
@daytona250: using a % at the front of a like statement will not be able to use indexes in an efficient way (e.g. an index seek), but can still use an Index scan, which is a whole lot better than a table scan.
Teevus
@George2, I meant add another select after the existing select, not a subquery. As far as count(*) statement touching all records, this is highly unlikely unless the query optimizer decides to do a table scan, which shouldn't happen if you have appropriate indexes on the foo column. Because it has the same "where" clause as your select statement, the select count(*) statement will most likely use the same index, or may even find a better one as it doesn't need to return data from any columns on the mycorp table.
Teevus