What is the best way to paginate results in SQL Server

前端 未结 19 2362
我寻月下人不归
我寻月下人不归 2020-11-22 01:36

What is the best way (performance wise) to paginate results in SQL Server 2000, 2005, 2008, 2012 if you also want to get the total number of results (before paginating)?

相关标签:
19条回答
  • 2020-11-22 01:53
    create PROCEDURE SP_Company_List (@pagesize int = -1 ,@pageindex int= 0   ) > AS BEGIN  SET NOCOUNT ON;
    
    
        select  Id , NameEn     from Company  ORDER by Id ASC  
    OFFSET (@pageindex-1 )* @pagesize   ROWS FETCH NEXt @pagesize ROWS ONLY END  GO
    

    DECLARE   @return_value int
    
    EXEC  @return_value = [dbo].[SP_Company_List]         @pagesize = 1 ,         > @pageindex = 2
    
    SELECT    'Return Value' = @return_value
    
    GO
    
    0 讨论(0)
  • 2020-11-22 01:57

    This is a duplicate of the 2012 old SO question: efficient way to implement paging

    FROM [TableX] ORDER BY [FieldX] OFFSET 500 ROWS FETCH NEXT 100 ROWS ONLY

    Here the topic is discussed in greater details, and with alternate approaches.

    0 讨论(0)
  • 2020-11-22 01:58

    Getting the total number of results and paginating are two different operations. For the sake of this example, let's assume that the query you're dealing with is

    SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate
    

    In this case, you would determine the total number of results using:

    SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'
    

    ...which may seem inefficient, but is actually pretty performant, assuming all indexes etc. are properly set up.

    Next, to get actual results back in a paged fashion, the following query would be most efficient:

    SELECT  *
    FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
              FROM      Orders
              WHERE     OrderDate >= '1980-01-01'
            ) AS RowConstrainedResult
    WHERE   RowNum >= 1
        AND RowNum < 20
    ORDER BY RowNum
    

    This will return rows 1-19 of the original query. The cool thing here, especially for web apps, is that you don't have to keep any state, except the row numbers to be returned.

    0 讨论(0)
  • 2020-11-22 02:00
       CREATE view vw_sppb_part_listsource as 
        select row_number() over (partition by sppb_part.init_id order by sppb_part.sppb_part_id asc ) as idx, * from (
          select 
              part.SPPB_PART_ID
              , 0 as is_rev
              , part.part_number 
              , part.init_id 
          from t_sppb_init_part part 
          left join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
          where prev.SPPB_PART_ID is null 
          union 
          select 
              part.SPPB_PART_ID
              , 1 as is_rev
              , prev.part_number 
              , part.init_id 
          from t_sppb_init_part part 
          inner join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
        ) sppb_part
    

    will restart idx when it comes to different init_id

    0 讨论(0)
  • 2020-11-22 02:02

    Well I have used the following sample query in my SQL 2000 database, it works well for SQL 2005 too. The power it gives you is dynamically order by using multiple columns. I tell you ... this is powerful :)

        ALTER PROCEDURE [dbo].[RE_ListingReports_SelectSummary] 
    
    @CompanyID  int,
    @pageNumber     int,
    @pageSize   int, 
    @sort       varchar(200)
    AS
    
    DECLARE @sql nvarchar(4000)
    DECLARE @strPageSize nvarchar(20)
    DECLARE @strSkippedRows nvarchar(20)
    DECLARE @strFields nvarchar(4000)
    DECLARE @strFilter nvarchar(4000)
    DECLARE @sortBy nvarchar(4000)
    DECLARE @strFrom nvarchar(4000)
    DECLARE @strID nvarchar(100)
    
    If(@pageNumber < 0)
      SET @pageNumber = 1
    SET @strPageSize = CAST(@pageSize AS varchar(20)) 
    SET @strSkippedRows = CAST(((@pageNumber - 1) * @pageSize) AS varchar(20))-- For    example if pageNumber is 5  pageSize is 10, then SkippedRows = 40.
    SET @strID = 'ListingDbID'
    SET @strFields = 'ListingDbID,
    ListingID,  
    [ExtraRoom]
    '
    SET @strFrom = ' vwListingSummary '
    
    SET @strFilter = ' WHERE
            CompanyID = ' + CAST(@CompanyID As varchar(20)) 
    End
    SET @sortBy = ''
    if(len(ltrim(rtrim(@sort))) > 0)
    SET @sortBy = ' Order By ' + @sort
    
    -- Total Rows Count
    
    SET @sql =  'SELECT Count(' + @strID + ')  FROM ' + @strFROM + @strFilter
    EXEC sp_executesql @sql
    
    --// This technique is used in a Single Table pagination
    SET @sql = 'SELECT ' + @strFields + ' FROM ' + @strFROM +
        ' WHERE ' + @strID +  ' IN ' + 
       '  (SELECT TOP ' + @strPageSize + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + 
                 ' AND  ' + @strID + ' NOT IN ' + '
              (SELECT TOP ' + @strSkippedRows + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + @SortBy + ') ' 
       + @SortBy + ') ' + @SortBy
    Print @sql 
    EXEC sp_executesql @sql
    

    The best part is sp_executesql caches later calls, provided you pass same parameters i.e generate same sql text.

    0 讨论(0)
  • 2020-11-22 02:03

    These are my solutions for paging the result of query in SQL server side. these approaches are different between SQL Server 2008 and 2012. Also, I have added the concept of filtering and order by with one column. It is very efficient when you are paging and filtering and ordering in your Gridview.

    Before testing, you have to create one sample table and insert some row in this table : (In real world you have to change Where clause considering your table fields and maybe you have some join and subquery in main part of select)

    Create Table VLT
    (
        ID int IDentity(1,1),
        Name nvarchar(50),
        Tel Varchar(20)
    )
    GO
    
    
    Insert INTO VLT
    VALUES
        ('NAME' + Convert(varchar(10),@@identity),'FAMIL' + Convert(varchar(10),@@identity))
    GO 500000
    

    In all of these sample, I want to query 200 rows per page and I am fetching the row for page number 1200.

    In SQL server 2008, you can use the CTE concept. Because of that, I have written two type of query for SQL server 2008+

    -- SQL Server 2008+

    DECLARE @PageNumber Int = 1200
    DECLARE @PageSize INT = 200
    DECLARE @SortByField int = 1 --The field used for sort by
    DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
    DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
    DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
    DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.
    
    SELECT 
      Data.ID,
      Data.Name,
      Data.Tel
    FROM
      (  
        SELECT 
          ROW_NUMBER() 
            OVER( ORDER BY 
                    CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                          THEN VLT.ID END ASC,
                    CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                          THEN VLT.ID END DESC,
                    CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                          THEN VLT.Name END ASC,
                    CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                          THEN VLT.Name END ASC,
                    CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                          THEN VLT.Tel END ASC,
                    CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                          THEN VLT.Tel END ASC
             ) AS RowNum
          ,*  
        FROM VLT 
        WHERE
          ( -- We apply the filter logic here
            CASE
              WHEN @FilterType = 'None' THEN 1
    
              -- Name column filter
              WHEN @FilterType = 'Contain' AND @FilterColumn = 1
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.ID LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.ID NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 1
                AND VLT.ID = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
                AND VLT.ID <> @FilterValue THEN 1               
    
              -- Name column filter
              WHEN @FilterType = 'Contain' AND @FilterColumn = 2
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Name LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Name NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 2
                AND VLT.Name = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
                AND VLT.Name <> @FilterValue THEN 1         
    
             -- Tel column filter   
             WHEN @FilterType = 'Contain' AND @FilterColumn = 3
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Tel LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Tel NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 3
                AND VLT.Tel = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
                AND VLT.Tel <> @FilterValue THEN 1    
    
            END
          ) = 1   
      ) AS Data
    WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
      AND Data.RowNum <= @PageSize * @PageNumber
    ORDER BY Data.RowNum
    
    GO
    

    And second solution with CTE in SQL server 2008+

    DECLARE @PageNumber Int = 1200
    DECLARE @PageSize INT = 200
    DECLARE @SortByField int = 1 --The field used for sort by
    DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
    DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
    DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
    DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.
    
    ;WITH
      Data_CTE
      AS
      (  
        SELECT 
          ROW_NUMBER() 
            OVER( ORDER BY 
                    CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                          THEN VLT.ID END ASC,
                    CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                          THEN VLT.ID END DESC,
                    CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                          THEN VLT.Name END ASC,
                    CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                          THEN VLT.Name END ASC,
                    CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                          THEN VLT.Tel END ASC,
                    CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                          THEN VLT.Tel END ASC
             ) AS RowNum
          ,*  
        FROM VLT
        WHERE
          ( -- We apply the filter logic here
            CASE
              WHEN @FilterType = 'None' THEN 1
    
              -- Name column filter
              WHEN @FilterType = 'Contain' AND @FilterColumn = 1
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.ID LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.ID NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 1
                AND VLT.ID = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
                AND VLT.ID <> @FilterValue THEN 1               
    
              -- Name column filter
              WHEN @FilterType = 'Contain' AND @FilterColumn = 2
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Name LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Name NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 2
                AND VLT.Name = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
                AND VLT.Name <> @FilterValue THEN 1         
    
             -- Tel column filter   
             WHEN @FilterType = 'Contain' AND @FilterColumn = 3
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Tel LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Tel NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 3
                AND VLT.Tel = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
                AND VLT.Tel <> @FilterValue THEN 1    
    
            END
          ) = 1     
      )
    
    SELECT 
      Data.ID,
      Data.Name,
      Data.Tel
    FROM Data_CTE AS Data
    WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
      AND Data.RowNum <= @PageSize * @PageNumber
    ORDER BY Data.RowNum
    

    -- SQL Server 2012+

    DECLARE @PageNumber Int = 1200
    DECLARE @PageSize INT = 200
    DECLARE @SortByField int = 1 --The field used for sort by
    DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
    DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
    DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
    DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.
    
    ;WITH
      Data_CTE
      AS
      (  
        SELECT 
          *  
        FROM VLT
        WHERE
          ( -- We apply the filter logic here
            CASE
              WHEN @FilterType = 'None' THEN 1
    
              -- Name column filter
              WHEN @FilterType = 'Contain' AND @FilterColumn = 1
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.ID LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.ID NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 1
                AND VLT.ID = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
                AND VLT.ID <> @FilterValue THEN 1               
    
              -- Name column filter
              WHEN @FilterType = 'Contain' AND @FilterColumn = 2
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Name LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Name NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 2
                AND VLT.Name = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
                AND VLT.Name <> @FilterValue THEN 1         
    
             -- Tel column filter   
             WHEN @FilterType = 'Contain' AND @FilterColumn = 3
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Tel LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
                AND ( -- In this case, when the filter value is empty, we want to show everything.
                    VLT.Tel NOT LIKE '%' + @FilterValue + '%'
                   OR
                    @FilterValue = ''
                   ) THEN 1
              WHEN @FilterType = 'Match' AND @FilterColumn = 3
                AND VLT.Tel = @FilterValue THEN 1
              WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
                AND VLT.Tel <> @FilterValue THEN 1    
    
            END
          ) = 1         
      )
    
    SELECT 
      Data.ID,
      Data.Name,
      Data.Tel
    FROM Data_CTE AS Data
    ORDER BY 
        CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
            THEN Data.ID END ASC,
        CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
            THEN Data.ID END DESC,
        CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
            THEN Data.Name END ASC,
        CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
            THEN Data.Name END ASC,
        CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
            THEN Data.Tel END ASC,
        CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
            THEN Data.Tel END ASC
    OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY;
    
    0 讨论(0)
提交回复
热议问题