在各版本 SQL Server 中使用行偏移与分页技术
节80.1:使用 OFFSET FETCH 分页
适用版本 ≥ SQL Server 2012
OFFSET FETCH 子句以更简洁的方式实现分页:跳过 N₁ 行(OFFSET 指定)后返回接下来的 N₂ 行(FETCH 指定)。
sql
SELECT *
FROM sys.objects
ORDER BY object_id
OFFSET 40 ROWS
FETCH NEXT 10 ROWS ONLY;
ORDER BY 子句必须存在,以保证确定性顺序。
节80.2:使用内层查询分页
在较早版本的 SQL Server 中,需结合双重排序与 TOP 关键字实现分页:
sql
SELECT TOP 10 *
FROM (
SELECT TOP 50
object_id,
name,
type,
create_date
FROM sys.objects
ORDER BY name ASC
) AS data
ORDER BY name DESC;
内层查询先按 name 升序取前 50 行;外层再按 name 降序取前 10 行,即得到原序列的第 41--50 行。
节80.3:各版本 SQL Server 分页写法
SQL Server 2012/2014
sql
DECLARE @RowsPerPage INT = 10,
@PageNumber INT = 4;
SELECT OrderId, ProductId
FROM OrderDetail
ORDER BY OrderId
OFFSET (@PageNumber - 1) * @RowsPerPage ROWS
FETCH NEXT @RowsPerPage ROWS ONLY;
SQL Server 2005/2008/R2
sql
DECLARE @RowsPerPage INT = 10,
@PageNumber INT = 4;
SELECT OrderId, ProductId
FROM (
SELECT
OrderId,
ProductId,
ROW_NUMBER() OVER (ORDER BY OrderId) AS RowNum
FROM OrderDetail
) AS OD
WHERE OD.RowNum BETWEEN ((@PageNumber - 1) * @RowsPerPage) + 1
AND @RowsPerPage * @PageNumber;
SQL Server 2000
sql
DECLARE @RowsPerPage INT = 10,
@PageNumber INT = 4;
SELECT OrderId, ProductId
FROM (
SELECT TOP (@RowsPerPage)
OrderId, ProductId
FROM (
SELECT TOP ((@PageNumber) * @RowsPerPage)
OrderId, ProductId
FROM OrderDetail
ORDER BY OrderId
) AS OD
ORDER BY OrderId DESC
) AS OD2
ORDER BY OrderId ASC;
节80.4:SQL Server 2012/2014 使用 ORDER BY OFFSET 与 FETCH NEXT
获取接下来 10 行:
sql
SELECT *
FROM TableName
ORDER BY id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
要点:
- ORDER BY 必须存在,才能使用 OFFSET 与 FETCH。
- OFFSET 子句必须与 FETCH 一起出现;不能单独使用
ORDER BY ... FETCH。 - 同一查询中不可将 TOP 与 OFFSET/FETCH 混用。
节80.5:使用 ROW_NUMBER 与公用表表达式实现分页
适用版本 ≥ SQL Server 2008
ROW_NUMBER 函数可为结果集的每一行分配递增序号,结合公用表表达式 (CTE) 与 BETWEEN 运算符,可得到"页"结果:
sql
WITH data AS (
SELECT
ROW_NUMBER() OVER (ORDER BY name) AS row_id,
object_id,
name,
type,
create_date
FROM sys.objects
)
SELECT *
FROM data
WHERE row_id BETWEEN 41 AND 50;
注意:ROW_NUMBER 不能直接用于 WHERE 子句,如:
sqlSELECT object_id, name, type, create_date FROM sys.objects WHERE ROW_NUMBER() OVER (ORDER BY name) BETWEEN 41 AND 50执行将报错:
消息 4108,级别 15,状态 1,行 6 窗口函数只能出现在 SELECT 或 ORDER BY 子句中。