如果您还想获得结果总数(在分页之前),那么在SQL Server 2000,2005,2008,2012中分页结果的最佳方法(性能明智)是什么?
最后,Microsoft SQL Server 2012发布了,我真的很喜欢它的分页简单,你不必使用这里回答的复杂查询.
要获取接下来的10行,只需运行此查询:
SELECT * FROM TableName ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
http://technet.microsoft.com/en-us/library/gg699618.aspx
使用时需要考虑的要点:
ORDER BY必须使用OFFSET和FETCH子句.
FETCH强制使用OFFSET子句.你永远不能使用,ORDER BY ... FETCH.
TOP不能与OFFSET和FETCH在同一查询表达式中组合使用.
获得结果总数和分页是两种不同的操作.为了这个例子,让我们假设您正在处理的查询是
SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate
在这种情况下,您将使用以下内容确定结果总数:
SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'
...假设所有索引等都已正确设置,这看起来效率低下,但实际上非常高效.
接下来,为了以分页方式返回实际结果,以下查询将是最有效的:
SELECT * FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, * FROM Orders WHERE OrderDate >= '1980-01-01' ) AS RowConstrainedResult WHERE RowNum >= 1 AND RowNum < 20 ORDER BY RowNum
这将返回原始查询的第1-19行.这里很酷的东西,特别是对于web应用程序,是你不必保留任何状态,除了要返回的行号.
令人难以置信的是,没有其他答案提到在所有SQL Server版本中进行分页的最快方法.对于大页码,偏移量可能非常慢,这是基准测试.在SQL中执行分页有一种完全不同的,更快的方式.这通常被称为"搜索方法"或"密钥集分页",如此博客文章中所述.
SELECT TOP 10 first_name, last_name, score, COUNT(*) OVER() 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应用程序中延迟加载更多数据时实现分页的最佳方法.
注意,"搜索方法"也称为键集分页.
该COUNT(*) OVER()
窗口功能将帮助你"分页之前"算的总记录数.如果您使用的是SQL Server 2000,则必须使用两个查询COUNT(*)
.
从SQL Server 2012开始,我们可以使用OFFSET
和FETCH NEXT
Clause来实现分页.
试试这个,对于SQL Server:
在SQL Server 2012中,ORDER BY子句中添加了一项新功能,用于查询集合数据的优化,使用T-SQL以及SQL Server中的整个执行计划编写任何人的数据分页,使工作更轻松.
在T-SQL脚本下面,使用与前一个示例中使用的逻辑相同的逻辑.
--CREATING A PAGING WITH OFFSET and FETCH clauses IN "SQL SERVER 2012" DECLARE @PageNumber AS INT, @RowspPage AS INT SET @PageNumber = 2 SET @RowspPage = 10 SELECT ID_EXAMPLE, NM_EXAMPLE, DT_CREATE FROM TB_EXAMPLE ORDER BY ID_EXAMPLE OFFSET ((@PageNumber - 1) * @RowspPage) ROWS FETCH NEXT @RowspPage ROWS ONLY;
TechNet:使用SQL Server分页查询
有关不同分页技术的概述,请访问http://www.codeproject.com/KB/aspnet/PagingLarge.aspx
我经常使用ROWCOUNT方法主要使用SQL Server 2000(也可以使用2005和2008,只测量性能与ROW_NUMBER相比),它快速闪电,但你需要确保排序的列(主要是) )独特的价值观.
MSDN:ROW_NUMBER(Transact-SQL)
返回结果集分区中行的序号,从1开始,每个分区的第一行.
以下示例按OrderDate的顺序返回编号为50到60的行.
WITH OrderedOrders AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY FirstName DESC) AS RowNumber,
FirstName, LastName, ROUND(SalesYTD,2,1) AS "Sales YTD"
FROM [dbo].[vSalesPerson]
)
SELECT RowNumber,
FirstName, LastName, Sales YTD
FROM OrderedOrders
WHERE RowNumber > 50 AND RowNumber < 60;
RowNumber FirstName LastName SalesYTD --- ----------- ---------------------- ----------------- 1 Linda Mitchell 4251368.54 2 Jae Pak 4116871.22 3 Michael Blythe 3763178.17 4 Jillian Carson 3189418.36 5 Ranjit Varkey Chudukatil 3121616.32 6 José Saraiva 2604540.71 7 Shu Ito 2458535.61 8 Tsvi Reiter 2315185.61 9 Rachel Valdez 1827066.71 10 Tete Mensa-Annan 1576562.19 11 David Campbell 1573012.93 12 Garrett Vargas 1453719.46 13 Lynn Tsoflias 1421810.92 14 Pamela Ansman-Wolfe 1352577.13
对于SQL Server 2000,您可以使用带有IDENTITY列的表变量来模拟ROW_NUMBER():
DECLARE @pageNo int -- 1 based DECLARE @pageSize int SET @pageNo = 51 SET @pageSize = 20 DECLARE @firstRecord int DECLARE @lastRecord int SET @firstRecord = (@pageNo - 1) * @pageSize + 1 -- 1001 SET @lastRecord = @firstRecord + @pageSize - 1 -- 1020 DECLARE @orderedKeys TABLE ( rownum int IDENTITY NOT NULL PRIMARY KEY CLUSTERED, TableKey int NOT NULL ) SET ROWCOUNT @lastRecord INSERT INTO @orderedKeys (TableKey) SELECT ID FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate SET ROWCOUNT 0 SELECT t.* FROM Orders t INNER JOIN @orderedKeys o ON o.TableKey = t.ID WHERE o.rownum >= @firstRecord ORDER BY o.rownum
这种方法可以扩展到具有多列密钥的表,并且不会产生使用OR(跳过索引使用)的性能开销.缺点是如果数据集非常大且一个接近最后一页,则用尽的临时空间量.在这种情况下我没有测试游标性能,但它可能会更好.
请注意,可以针对第一页数据优化此方法.此外,由于TOP不接受SQL Server 2000中的变量,因此使用了ROWCOUNT.