我必须处理一个可能很大的记录列表,我一直在谷歌搜索避免选择整个列表的方法,而是我想让用户选择一个页面(如1到10)并相应地显示记录
说,对于1000条记录,我将拥有100页,每条10条记录,最先显示10条记录,如果用户点击第5页,它将显示41到50条记录。
为每条记录添加行号然后根据行号查询是否是一个好主意?有没有更好的方法来实现分页结果而没有太多的开销? 到目前为止,这里描述的方法看起来最有希望:
http://developer.berlios.de/docman/display_doc.php?docid=739&group_id=2899
答案 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)
其他人已经解释了如何使用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会在极端情况下更好地执行它。
在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