fre*_*red 10 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
Roa*_*ior 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
Run Code Online (Sandbox Code Playgroud)
如果您使用带有两个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
Run Code Online (Sandbox Code Playgroud)
其他人已经解释了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
Run Code Online (Sandbox Code Playgroud)
如果您正在使用SQL Server 2012并且向后兼容性不是问题,那么您应该更喜欢这个子句,因为SQL Server在极端情况下会更好地执行它.
在SQL中执行分页有一种完全不同的,更快的方法.这通常被称为"搜索方法",如本博客文章中所述.
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
Run Code Online (Sandbox Code Playgroud)
该@previousScore
和@previousPlayerId
值是来自前一页的最后一条记录的相应值.这允许您获取"下一页".如果ORDER BY
方向是ASC
,只需使用>
.
使用上述方法,您无法在未先读取前40条记录的情况下立即跳转到第4页.但通常情况下,你不想跳得那么远.相反,您可以获得更快的查询,该查询可能能够在固定时间内获取数据,具体取决于您的索引.此外,无论基础数据是否发生变化,您的页面都将保持"稳定"状态(例如,在第4页上,当您在第4页时).
例如,这是在Web应用程序中延迟加载更多数据时实现分页的最佳方法.
注意,"搜索方法"也称为键集寻呼.
尝试这样的事情:
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
Run Code Online (Sandbox Code Playgroud)