分页查询必须在GROUP BY之后、ORDER BY之后执行,否则结果不可靠;正确顺序是WHERE→GROUP BY→HAVING→ORDER BY→LIMIT/OFFSET,且排序字段须出现在SELECT中。

分页查询必须在分组之后做
不能直接对 GROUP BY 结果加 LIMIT,因为分组本身不保证顺序,且分页逻辑依赖于确定的行序。你得先完成分组聚合、排序,再分页——否则可能漏掉组、重复取组,或页与页之间数据错乱。
典型错误写法:SELECT sex, AVG(math) FROM stu GROUP BY sex LIMIT 0,2 —— 这看似“取前两组”,但 MySQL 不保证 GROUP BY 输出顺序,且没 ORDER BY 时结果不可预测,换次执行可能返回不同性别组。
- 必须显式加
ORDER BY(比如按平均分、人数、分组字段),否则LIMIT行为无意义 -
LIMIT和OFFSET是最后执行的步骤,只能作用于最终结果集,不是中间分组过程 - 如果要跳过前 N 个分组,
OFFSET值是组数,不是原始行数
MySQL 8.0+ 推荐用 OFFSET + LIMIT 组合
这是最直观、兼容性好、语义清晰的方式。注意:排序字段必须出现在 SELECT 列表中,或至少是分组字段/聚合结果。
SELECT sex, AVG(math) AS avg_score, COUNT(*) AS cnt FROM stu WHERE math > 60 GROUP BY sex ORDER BY avg_score DESC LIMIT 10 OFFSET 0;
上面查第 1 页(每页 10 个分组);第 2 页就把 OFFSET 改成 10,依此类推。
-
OFFSET超出总分组数时,返回空结果集,不会报错 - 性能隐患:当
OFFSET很大(比如几万),MySQL 仍需扫描并跳过前面所有分组,建议配合覆盖索引或改用游标分页 - 别用
LIMIT m,n旧写法(如LIMIT 0,10),它和LIMIT 10 OFFSET 0等价,但可读性差,且容易混淆起始位置
HAVING 过滤后才能分页
想只对“人数 ≥ 2 的性别组”分页?必须把条件写在 HAVING,而不是 WHERE。因为 WHERE 在分组前过滤原始行,HAVING 才能基于聚合结果筛选分组。
SELECT sex, AVG(math) AS avg_score, COUNT(*) AS cnt FROM stu GROUP BY sex HAVING COUNT(*) >= 2 ORDER BY cnt DESC LIMIT 5 OFFSET 0;
-
HAVING COUNT(*) >= 2是合法的;WHERE COUNT(*) >= 2会报错Invalid use of group function -
HAVING过滤发生在分组和聚合之后、ORDER BY和LIMIT之前,所以分页对象是过滤后的分组集合 - 如果同时有
WHERE和HAVING,执行顺序是:WHERE → GROUP BY → 聚合 → HAVING → ORDER BY → LIMIT/OFFSET
大数据量下分页聚合结果要小心
当分组数达到几千甚至上万,OFFSET 分页会越来越慢,因为 MySQL 每次都要重新执行完整分组、排序,再跳过前 N 组。这不是语法问题,是执行模型限制。
- 避免
SELECT *后再GROUP BY:只选真正需要的分组字段和聚合字段,减少内存和临时表开销 - 确保
GROUP BY字段有索引,尤其复合索引要覆盖WHERE条件 + 分组字段 +ORDER BY字段 - 真要处理海量分组页码(比如后台导出分页报表),考虑用游标分页:记录上一页最后一个分组的排序值(如上页末尾的
avg_score),下一页用WHERE avg_score + <code>LIMIT,绕过OFFSET
最易被忽略的一点:分组分页的本质是「对聚合结果集分页」,不是「对原始表分页」。很多人试图在子查询里先 LIMIT 再 GROUP BY,那只会分组部分数据,统计完全失真。

















