SQL中同时使用GROUP BY与ORDER BY要注意什么?

作者:袖梨 2026-07-21
GROUP BY 和 ORDER BY 不能直接组合实现“每组取最新/最高一条”,因 GROUP BY 先执行且只保留每组首行,ORDER BY 仅对其排序;正确方案是窗口函数或关联子查询。

GROUP BY 和 ORDER BY 不能随便连用,否则查出来的不是你想要的那条记录——尤其是想“每组取最新/最高的一条”时,几乎必然出错。

ORDER BY 字段必须在 GROUP BY 列表里或套聚合函数

MySQL 5.7+ 默认开启 ONLY_FULL_GROUP_BY,只要 ORDER BY 后的字段没出现在 GROUP BY 子句中,也没被 MAX()COUNT() 等包裹,就会直接报错:Expression #1 of ORDER BY clause is not in GROUP BY clause

  • 错误写法:SELECT examId, score FROM ko_monthly_exam_history GROUP BY examId ORDER BY score DESCscore 既没分组也没聚合)
  • 正确写法(聚合兜底):SELECT examId, MAX(score) AS score FROM ko_monthly_exam_history GROUP BY examId ORDER BY score DESC
  • 正确写法(扩展分组):SELECT examId, score FROM ko_monthly_exam_history GROUP BY examId, score ORDER BY score DESC(但语义已变,不再是“每组一条”,而是去重组合)

GROUP BY 后的 ORDER BY 不会帮你挑组内最大/最新记录

GROUP BY 先执行,只保留每组“第一行”(物理顺序或引擎默认行为),ORDER BY 对的是这“一行”的字段再排一次序,跟组内其他数据完全无关。

  • 现象:SELECT id, examId, score FROM t GROUP BY examId ORDER BY score DESC 返回的 id 很可能对应的是该组低分记录,不是最高分那条
  • 根本原因:执行顺序是 FROM → WHERE → GROUP BY → SELECT → ORDER BYORDER BY 根本看不到组内全量数据
  • 别信“子查询先 ORDER BYGROUP BY”:MySQL 5.7+ 优化器会丢弃子查询里的 ORDER BY,除非加 LIMIT 999999999 强制保留(不推荐)

真正要取每组最新/最高记录,得用窗口函数或关联子查询

想按 examId 分组,取每组 score 最高且 time 最新的那条完整记录,必须跳出 GROUP BY 的限制。

  • MySQL 8.0+ 推荐窗口函数:SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY examId ORDER BY score DESC, time DESC) AS rn FROM ko_monthly_exam_history) t WHERE t.rn = 1
  • 兼容老版本用关联子查询:SELECT t1.* FROM ko_monthly_exam_history t1 WHERE t1.id = (SELECT id FROM ko_monthly_exam_history t2 WHERE t2.examId = t1.examId ORDER BY score DESC, time DESC LIMIT 1)
  • 注意子查询加 LIMIT 的坑:SELECT * FROM (SELECT * FROM t ORDER BY score DESC LIMIT 1000) s GROUP BY examId 可能漏掉没进前 1000 的 examIdLIMIT 必须放最外层

ORDER BY 字段没索引会导致排序失效

即使语法合法、逻辑正确,如果 ORDER BY 字段没索引,MySQL 会走 Using filesort,结果顺序看似随机,性能也差。

  • EXPLAINExtra 列:出现 Using filesort 就说明没走索引排序
  • ORDER BY 字段尽量落在驱动表(FROM 后第一张表)上
  • 复合索引更稳妥:比如常按 examId 分组、再按 MAX(time) 排序,建 INDEX idx_exam_time (examId, time)
  • 别写 ORDER BY 2 这类位置引用——字段一增减就全乱

最容易被忽略的点是:你以为 GROUP BY + ORDER BY 能取每组最优记录,其实它只负责把组排个序;真正筛选组内数据,得靠窗口函数、子查询或业务层二次处理。别让 ONLY_FULL_GROUP_BY 报错把你骗过去——报错只是表象,逻辑错才是根因。

相关文章

精彩推荐