SQL Server 2008的分页方法?

时间:2010-12-05 10:00:09

标签: sql sql-server performance pagination

我必须处理一个可能很大的记录列表,我一直在谷歌搜索避免选择整个列表的方法,而是我想让用户选择一个页面(如1到10)并相应地显示记录

说,对于1000条记录,我将拥有100页,每条10条记录,最先显示10条记录,如果用户点击第5页,它将显示41到50条记录。

为每条记录添加行号然后根据行号查询是否是一个好主意?有没有更好的方法来实现分页结果而没有太多的开销? 到目前为止,这里描述的方法看起来最有希望:

http://developer.berlios.de/docman/display_doc.php?docid=739&group_id=2899

http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

7 个答案:

答案 0 :(得分:15)

以下T-SQL存储过程是一种非常高效的分页实现。 SQL优化器可以非常快速地找到第一个ID。将此与ROWCOUNT结合使用,您就拥有了一种既节省CPU又具有读取效率的方法。对于具有大量行的表,它肯定胜过我使用临时表或表变量看到的任何方法。

注意:我在此示例中使用了顺序标识列,但代码适用于任何适合页面排序的列。此外,正在使用的列中的序列中断不会影响结果,因为代码会选择多个行而不是列值。

编辑:如果您对具有可能非唯一值的列(例如LastName)进行排序,则在Order By子句中添加第二列,以使排序值再次唯一。

CREATE  PROCEDURE dbo.PagingTest
(
    @PageNumber int,
    @PageSize int
)
AS

DECLARE @FirstId int, @FirstRow int

SET @FirstRow = ( (@PageNumber - 1) * @PageSize ) + 1
SET ROWCOUNT @FirstRow

-- Add check here to ensure that @FirstRow is not
-- greater than the number of rows in the table.

SELECT   @FirstId = [Id]
FROM     dbo.TestTable
ORDER BY [Id]

SET ROWCOUNT @PageSize

SELECT   *
FROM     dbo.TestTable
WHERE    [Id] >= @FirstId
ORDER BY [Id]

SET ROWCOUNT 0
GO 

答案 1 :(得分:7)

如果您使用带有两个row_number()列的CTE - 一个已排序的asc,一个desc,您将通过添加两个row_number列获取分页的行号以及总记录。

create procedure get_pages(@page_number int, @page_length int)
as
    set nocount on;

    with cte as
    (
        select 
            Row_Number() over (order by sort_column desc) as row_num
            ,Row_Number() over (order by sort_column) as inverse_row_num
            ,id as cte_id
        From my_table
    )
    Select 
        row_num+inverse_row_num as total_rows
        ,*  
    from CTE inner join my_table
        on cte_id=df_messages.id
    where row_num between 
        (@page_number)*@page_length 
        and (@page_number+1)*@page_length
    order by rownumber

答案 2 :(得分:5)

使用OFFSET

其他人已经解释了如何使用ROW_NUMBER() OVER()排名功能来执行页面。值得一提的是,SQL Server 2012最终包含对SQL标准OFFSET .. FETCH子句的支持:

SELECT first_name, last_name, score
FROM players
ORDER BY score DESC
OFFSET 40 ROWS FETCH NEXT 10 ROWS ONLY

如果您正在使用SQL Server 2012并且向后兼容性不是问题,那么您可能更喜欢这个子句,因为SQL Server会在极端情况下更好地执行它。

使用SEEK方法

在SQL中执行分页有一种完全不同的,快得多的方法。这通常被称为“寻找方法”,如this blog post here中所述。

SELECT TOP 10 first_name, last_name, score
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

@previousScore@previousPlayerId值是上一页中最后一条记录的相应值。这允许您获取“下一页”。如果ORDER BY方向为ASC,则只需使用>

使用上述方法,您无法在未先读取前40条记录的情况下立即跳转到第4页。但通常情况下,你不想跳得那么远。相反,您可以获得更快的查询,该查询可能能够在固定时间内获取数据,具体取决于您的索引。此外,无论基础数据是否发生变化,您的页面都将保持“稳定”状态(例如,在第1页上,当您在第4页时)。

例如,这是在网络应用程序中延迟加载更多数据时实现分页的最佳方式。

注意,“搜索方法”也称为keyset paging

答案 3 :(得分:4)

尝试这样的事情:

declare @page int = 2
declare @size int = 10

declare @lower int =  (@page - 1) * @size
declare @upper int =  (@page    ) * @size

select * from (
select 
    ROW_NUMBER() over (order by some_column) lfd,
* from your_table
) as t
 where lfd between @lower and @upper
 order by some_column

答案 4 :(得分:3)

这是使用TOP的@ RoadWarrior代码的更新版本。性能相同,速度极快。确保你有一个关于TestTable.ID的索引

CREATE PROC dbo.PagingTest
    @SkipRows int,
    @GetRows int
AS
DECLARE @FirstId int

SELECT   TOP (@SkipRows) 
         @FirstId = [Id]
FROM     dbo.TestTable
ORDER BY [Id]

SELECT   TOP (@GetRows) *
FROM     dbo.TestTable
WHERE    [Id] >= @FirstId
ORDER BY [Id]

GO 

答案 5 :(得分:0)

试试这个

Declare @RowStart int, @RowEnd int;


SET @RowStart = 4;
SET @RowEnd = 7; 

With MessageEntities As 
(
    Select ROW_NUMBER() Over (Order By [MESSAGE_ID]) As Row, [MESSAGE_ID]
    From [TBL_NAFETHAH_MESSAGES]
)
Select  m0.MESSAGE_ID, m0.MESSAGE_SENDER_NAME,
        m0.MESSAGE_SUBJECT, m0.MESSAGE_TEXT
From MessageEntities M
    Inner Join [TBL_NAFETHAH_MESSAGES] m0 on M.MESSAGE_ID = m0.MESSAGE_ID
Where M.Row Between @RowStart AND @RowEnd
Order By M.Row Asc
GO

答案 6 :(得分:0)

为什么不使用推荐的solution

  

选择VALUE产品FROM             AdventureWorksEntities.Products AS产品             按产品订购.ListPrice SKIP @skip LIMIT @limit