What is the best way to paginate results in SQL Server

前端 未结 19 2457
我寻月下人不归
我寻月下人不归 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 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;
    

提交回复
热议问题