许多应用程序都有网格,一次一页地显示数据库表中的数据.其中许多还允许用户选择每页的记录数,按任何列排序,并在结果中来回导航.
在不将整个表格带到客户端然后过滤客户端上的数据的情况下,实现此模式的好算法是什么.如何将您想要显示的记录带给用户?
LINQ是否简化了解决方案?
在MS SQL Server 2005及更高版本上,ROW_NUMBER()似乎工作:
T-SQL:使用ROW_NUMBER()进行分页
DECLARE @PageNum AS INT; DECLARE @PageSize AS INT; SET @PageNum = 2; SET @PageSize = 10; WITH OrdersRN AS ( SELECT ROW_NUMBER() OVER(ORDER BY OrderDate, OrderID) AS RowNum ,OrderID ,OrderDate ,CustomerID ,EmployeeID FROM dbo.Orders ) SELECT * FROM OrdersRN WHERE RowNum BETWEEN (@PageNum - 1) * @PageSize + 1 AND @PageNum * @PageSize ORDER BY OrderDate ,OrderID;
我建议使用LINQ,或尝试复制它的功能.我有一个应用程序,我使用LINQ Take和Skip方法来检索分页数据.代码看起来像这样:
MyDataContext db = new MyDataContext(); var results = db.Products .Skip((pageNumber - 1) * pageSize) .Take(pageSize);
运行SQL Server Profiler显示LINQ正在将此查询转换为SQL,类似于:
SELECT [ProductId], [Name], [Cost], and so on... FROM ( SELECT [ProductId], [Name], [Cost], [ROW_NUMBER] FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [Name]) AS [ROW_NUMBER], [ProductId], [Name], [Cost] FROM [Products] ) WHERE [ROW_NUMBER] BETWEEN 10 AND 20 ) ORDER BY [ROW_NUMBER]
简单来说:
1.过滤行并使用ROW_NUMBER函数按所需顺序添加行号.
2.过滤(1)以仅返回页面上所需的行号.
3.按行号排序(2),这与您想要的顺序相同(在本例中,按名称).
在数据库中基本上有两种分页方式(我假设你使用的是SQL Server):
其他人已经解释了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中执行分页有一种完全不同的,快得多但却鲜为人知的方法.这通常被称为"搜索方法",如本博客文章中所述.
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页.但通常情况下,你不想跳得那么远.相反,您可以获得更快的查询,该查询可能能够在固定时间内获取数据,具体取决于您的索引.此外,无论基础数据是否发生变化,您的页面都将保持"稳定"状态(例如,在第4页上,当您在第4页时).
例如,这是在Web应用程序中延迟加载更多数据时实现分页的最佳方法.
注意,"搜索方法"也称为键集寻呼.