mysql 5.7+ 默认启用 only_full_group_by,导致 group by 后 order by 非分组/非聚合字段报错;必须将排序字段加入 group by 或改用聚合表达式,且 group by 不保证顺序,须显式写 order by。

GROUP BY 后 ORDER BY 报 “is not in GROUP BY clause” 错误
MySQL 5.7+ 默认启用 ONLY_FULL_GROUP_BY,它会拒绝所有未出现在 GROUP BY 列表中、又不是聚合函数结果的 ORDER BY 字段。比如写 SELECT dept_id, COUNT(*) FROM employees GROUP BY dept_id ORDER BY created_at DESC,哪怕 created_at 在原表里存在,也会报错——因为分组后这列已不具确定性。
解决方法只有两条路:
- 把排序字段加进
GROUP BY(仅适用于你真想按该字段分组) - 改用聚合表达式,例如
ORDER BY MAX(created_at) DESC或ORDER BY AVG(salary) DESC
别关 ONLY_FULL_GROUP_BY。关掉它只是让错误静默,换到 PostgreSQL 或新版本 MySQL 就立刻崩。
想按聚合值倒序排,但用了别名却没生效
写 SELECT dept_id, AVG(salary) AS avg_salary FROM employees GROUP BY dept_id ORDER BY avg_salary DESC,在部分 MySQL 版本里会“看起来排好了”,但其实是侥幸——ORDER BY 实际执行时可能还没解析完 SELECT 中的别名。
最稳做法是直接复写表达式:
- ✅
ORDER BY AVG(salary) DESC - ✅
ORDER BY COUNT(*) DESC - ❌ 避免
ORDER BY avg_salary DESC(尤其嵌套查询或带HAVING时易失效)
如果非要用别名,必须包一层子查询:SELECT * FROM (SELECT dept_id, AVG(salary) AS avg_salary FROM employees GROUP BY dept_id) t ORDER BY avg_salary DESC。
要按业务自定义顺序排(比如 status = 'draft'→'confirmed'→'shipped')
FIELD() 能实现,但直接 ORDER BY FIELD(status, 'draft', 'confirmed', 'shipped') 在严格模式下可能被当作非确定性表达式而警告或拒绝。
推荐两种更稳妥的写法:
- 用
CASE WHEN:ORDER BY CASE status WHEN 'draft' THEN 1 WHEN 'confirmed' THEN 2 WHEN 'shipped' THEN 3 ELSE 4 END - 把
FIELD()套进聚合函数:ORDER BY MAX(FIELD(status, 'draft', 'confirmed', 'shipped'))(确保status在GROUP BY中)
FIELD() 对 NULL 返回 0,若 status 可能为空,得先 IFNULL(status, '') 处理,否则所有空值会挤在最前。
GROUP BY 结果看着乱,其实是根本没排序
GROUP BY 本身不保证任何输出顺序。你某次看到“按 dept_id 排好了”,只是碰巧——换索引、加 WHERE、甚至服务器负载高一点,顺序就变。这不是 bug,是 SQL 标准行为。
唯一可靠方式就是显式写 ORDER BY,且必须放在 GROUP BY 之后:
- ✅
SELECT dept_id, COUNT(*) FROM employees GROUP BY dept_id ORDER BY COUNT(*) DESC - ❌
SELECT dept_id, COUNT(*) FROM employees ORDER BY COUNT(*) DESC GROUP BY dept_id(语法错误) - ❌
ORDER BY 2(位置引用随SELECT改动立刻失效)
复杂点在于:如果 ORDER BY 字段来自 JOIN 的右表,又没索引,优化器可能退化成 Using filesort,排序性能骤降。这时候得检查 EXPLAIN 的 key 和 Extra 列,必要时给驱动表加复合索引。











