GROUP BY后不能直接分页,因SQL执行顺序要求ORDER BY必须在OFFSET/FETCH前且排序字段须在SELECT中可见;正确做法是用子查询或CTE封装聚合结果再排序分页。
聚合后不能直接分页,必须先封装结果再排序+分页,否则会报错或返回错乱数据。
SQL 执行顺序是 GROUP BY → HAVING → SELECT → ORDER BY → OFFSET → FETCH。如果在 GROUP BY 子句里就写 OFFSET,或者漏掉 ORDER BY,SQL Server 会直接拒绝执行:
Invalid usage of the option NEXT in the FETCH statementORDER BY is required in a query containing OFFSET更隐蔽的问题是:若 ORDER BY 引用的字段未出现在 SELECT 列表中(比如只写 ORDER BY SUM(amount),但外层没给它起别名),数据库也会报错——因为排序依据在逻辑上“不可见”。
这是最通用、最稳妥的方式,兼容 SQL Server 2012+、PostgreSQL、Oracle 12c+:
立即学习“前端免费学习笔记(深入)”;
SELECT category, total_sales, order_countFROM ( SELECT category, SUM(amount) AS total_sales, COUNT(*) AS order_count FROM orders GROUP BY category) AS groupedORDER BY total_sales DESC, categoryOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
AS grouped),否则 SQL Server 报 Incorrect syntax near 'OFFSET'
ORDER BY 必须引用外层列名(total_sales),不能写原始表达式(SUM(amount))category),避免相同 total_sales 值导致分页结果不稳定适用于 SQL Server 2005/2008,或需按多个聚合指标组合排序(如先按 COUNT(*) 降序,再按 AVG(price) 升序):
SELECT category, total_sales, order_countFROM ( SELECT category, SUM(amount) AS total_sales, COUNT(*) AS order_count, ROW_NUMBER() OVER ( ORDER BY COUNT(*) DESC, SUM(amount) DESC ) AS rn FROM orders GROUP BY category) AS rankedWHERE rn BETWEEN 21 AND 30ORDER BY rn;
ROW_NUMBER() 的 OVER 子句里可自由组合聚合表达式,灵活性远高于 OFFSET FETCH
WHERE 过滤的是行号,不是原始数据;且必须保留 ORDER BY rn 保证输出顺序ROW_NUMBER() 会为全量聚合结果编号,大数据量时比 OFFSET FETCH 更耗内存聚合分页真正的难点不在语法,而在语义一致性:
category —— 因为聚合值变了,排序位置就动了OFFSET 分页本质是“基于位置”,不是“基于内容”;要稳定分页,得配合快照隔离级别(SNAPSHOT ISOLATION)或应用层缓存聚合结果category),OFFSET 20 会直接返回空集,但错误不明显——得靠业务逻辑校验页码合法性