【问题标题】:How to do pagination in SQL Server 2008如何在 SQL Server 2008 中进行分页
【发布时间】:2011-01-15 16:54:10
【问题描述】:

如何在 SQL Server 2008 中进行分页?

【问题讨论】:

  • 没有 MSSQL 2008 这样的东西。你的意思是“SQL Server 2008”

标签: sql sql-server-2008 pagination


【解决方案1】:

你可以使用ROW_NUMBER():

返回结果集分区内行的序号,每个分区的第一行从 1 开始。

例子:

WITH CTEResults AS
(
    SELECT IDColumn, SomeField, DateField, ROW_NUMBER() OVER (ORDER BY DateField) AS RowNum
    FROM MyTable
)

SELECT * 
FROM CTEResults
WHERE RowNum BETWEEN 10 AND 20;

【讨论】:

    【解决方案2】:

    你可以试试

    DECLARE @Table TABLE(
            Val VARCHAR(50)
    )
    
    DECLARE @PageSize INT,
            @Page INT
    
    SELECT  @PageSize = 10,
            @Page = 2
    
    ;WITH PageNumbers AS(
            SELECT Val,
                    ROW_NUMBER() OVER(ORDER BY Val) ID
            FROM    @Table
    )
    SELECT  *
    FROM    PageNumbers
    WHERE   ID  BETWEEN ((@Page - 1) * @PageSize + 1)
            AND (@Page * @PageSize)
    

    【讨论】:

    • 我认为这段代码不起作用。你测试过这段代码吗? (它应该用“@Table”替换“PageNumbers”表。
    • @zeitgeist,它正在使用一个名为 PageNumbers 的 CTE。您是否按原样测试了代码?
    • 这个分页算法是错误的。对于第 1 页和第 2 页,它可以工作,但从第 3 页开始,它将开始跳过条目。第2页将从11转到20,但第3页将从22转到30跳过条目21
    【解决方案3】:

    SQL Server 2012 提供分页功能(请参阅http://www.codeproject.com/Articles/442503/New-features-for-database-developers-in-SQL-Server

    在SQL2008中你可以这样做:

    declare @rowsPerPage as bigint; 
    declare @pageNum as bigint; 
    set @rowsPerPage=25; 
    set @pageNum=10;   
    
    With SQLPaging As   ( 
        Select Top(@rowsPerPage * @pageNum) ROW_NUMBER() OVER (ORDER BY ID asc) 
        as resultNum, * 
        FROM Employee )
    select * from SQLPaging with (nolock) where resultNum > ((@pageNum - 1) * @rowsPerPage)
    

    久经考验!它始终如一地工作和扩展。

    【讨论】:

    • 如果去掉顶部,结果可能会有所不同。如果您删除顶部,此方法不保证性能相同的结果,除非表已按 id 排序。
    【解决方案4】:

    1) 创建虚拟数据

    CREATE TABLE #employee (EMPID INT IDENTITY, NAME VARCHAR(20))
    
    DECLARE @id INT = 1
    
    WHILE @id < 200
    
    BEGIN
    INSERT INTO #employee ( NAME ) VALUES ('employee_' + CAST(@id AS VARCHAR) )
    SET @id = @id + 1
    END
    

    2) 现在应用解决方案。

    这种情况假设 EMPID 是唯一且已排序的列。

    在课程外,您将在不同的列中应用它...

    DECLARE @pageSize INT = 20
    
    SELECT * FROM (
    
    SELECT *, PageNumber =  CEILING(CAST(EMPID AS FLOAT)/@pageSize)   
    FROM #employee
    ) MyQuery
    
    WHERE MyQuery.PageNumber = 1          
    

    【讨论】:

    • 可爱的方法,但添加新列可能是开销。
    【解决方案5】:

    这些是我在 SQL 服务器端对查询结果进行分页的解决方案。我已经添加了过滤和排序的概念。当您在 Gridview 中进行分页、过滤和排序时,它非常有效。

    在测试之前,您必须创建一个示例表并在该表中插入一些行:(在现实世界中,您必须考虑您的表字段来更改 Where 子句,也许您在选择的主要部分有一些连接和子查询)

    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
    

    在 SQL Server 2008 中,您可以使用 CTE 概念。因此,我为 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
    

    在 SQL Server 2008+ 中使用 CTE 的第二个解决方案

    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
    

    【讨论】:

      【解决方案6】:

      另一个至少从 SQL 2005 开始工作的解决方案是将 TOP 与 SELECT 子查询和 ORDER BY 子句一起使用。

      简而言之,检索每页 10 行的第 2 行与检索前 20 行中的最后 10 行相同。这意味着使用 ASC 顺序检索前 20 行,然后使用 DESC 顺序检索前 10 行,然后再使用 ASC 进行排序。

      示例:检索页面 2 行,每页 3 行

      create table test(id integer);
      insert into test values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
      
      select * 
          from (
              select top 2 * 
                  from (
                      select  top (4) * 
                          from test 
                          order by id asc) tmp1
                  order by id desc) tmp1 
          order by id asc
      

      【讨论】:

      • TOP 解决了第一页的问题,但没有解决下一页的问题……我需要在有 50 条记录的页面中请求数据。我用的第一页选择 TOP 50,好的,但是第二页呢?
      • 这就是我的示例显示的内容。获取第 2 页的 50 条记录:选择前 100 条记录,然后按倒序排列,选择前 50 条记录。您现在有您的第 2 页记录,但顺序相反。正确地重新排序它们,你就完成了。但那是复杂的代码,你应该只在你被旧的 SQL Server 版本卡住并且无法访问诸如 ROW_NUMBER() 之类的功能时才使用它。
      【解决方案7】:

      我用于分页的查询是这个(在 Oracle DB 上)。

      SELECT * FROM tableName
      WHERE  RowNum >= 1
      AND RowNum < 20
      ORDER BY RowNum;
      

      RowNum - 它是由 DB 提供的变量。对于查询返回的每一行,ROWNUM 伪列返回一个数字,指示 Oracle 从一个表或一组连接行中选择行的顺序。

      【讨论】:

        【解决方案8】:
        SELECT DISTINCT Id,ParticipantId,ActivityDate,IsApproved,
            IsDeclined,IsDeleted,SubmissionDate,    IsResubmitted,  
        
            [CategoryId] Id,[CategoryName] Name,
        
            [ActivityId] [Id],[ActivityName] Name,Points,   
        
            [UserId] [Id],Email,
            ROW_NUMBER() OVER(ORDER BY Id desc)   AS RowNum from
            (SELECT DISTINCT
            Id,ParticipantId,
            ActivityDate,IsApproved,
            IsDeclined,IsDeleted,
            SubmissionDate, IsResubmitted,  
        
            [CategoryId] [CategoryId],[CategoryName] [CategoryName],
        
            [ActivityId] [ActivityId],[ActivityName] [ActivityName],Points, 
        
            [UserId] [UserId],Email,
            ROW_NUMBER() OVER(ORDER BY Id desc)   AS RowNum from
        
             (SELECT DISTINCT ASN.Id,
            ASN.ParticipantId,ASN.ActivityDate,
            ASN.IsApproved,ASN.IsDeclined,
            ASN.IsDeleted,ASN.SubmissionDate,
            CASE WHEN (SELECT COUNT(*) FROM FDS_ActivitySubmission WHERE ParentId=ASN.Id)>0 THEN CONVERT(BIT, 1) ELSE CONVERT(BIT, 0) END IsResubmitted,
        
            AC.Id [CategoryId], AC.Name [CategoryName],
        
            A.Id [ActivityId],A.Name [ActivityName],A.Points,
        
            U.Id[UserId],U.Email    
        
        
        FROM
        FDS_ActivitySubmission ASN WITH (NOLOCK)
        INNER JOIN  
            FDS_ActivityCategory AC WITH (NOLOCK)
        ON 
            AC.Id=ASN.ActivityCategoryId
                INNER JOIN
            FDS_ApproverDetails FDSA
        ON
        FDSA.ParticipantID=ASN.ParticipantID
        
                INNER JOIN
               FDS_ActivityJobRole FAJ
        ON
             FAJ.RoleId=FDSA.JobRoleId
            INNER JOIN
        
            FDS_Activity A WITH (NOLOCK)
        ON 
            A.Id=ASN.ActivityId
        INNER JOIN
           Users U WITH (NOLOCK)
        ON
            ASN.ParticipantId=FDSA.ParticipantID
        WHERE
               IsDeclined=@IsDeclined AND IsApproved=@IsApproved    AND ASN.IsDeleted=0
               AND
               ISNULL(U.Id,0)=ISNULL(@ApproverId,0)
               AND ISNULL(ASN.IsDeleted,0)<>1)P)t where t.RowNum between 
               (((@PageNumber - 1) * @PageSize) + 1) AND (@PageNumber * PageSize)
            AND t.IsDeclined=@IsDeclined AND t.IsApproved=@IsApproved AND t.IsDeleted = 0
         AND (ISNULL(t.Id,0)=ISNULL(@SubmissionId,0)or ISNULL(@SubmissionId,0)<=0) 
        

        【讨论】:

        • 请不要只发布一些代码,而是解释为什么如何这段代码解决了这个问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-29
        • 1970-01-01
        • 1970-01-01
        • 2011-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多