GROUP BY 基本语法和必须遵守的字段规则
MySQL 中
GROUP BY要求:所有在
SELECT列表中出现的非聚合字段,必须出现在
GROUP BY子句中。否则会报错(5.7+ 默认 SQL 模式下)或返回不确定结果(旧版本或宽松模式)。 合法写法:
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id非法写法:
SELECT user_id, order_time, COUNT(*) FROM orders GROUP BY user_id(
order_time未聚合也未分组) 若真要查每个分组的某个时间,得明确语义,比如用
MAX(order_time)或
MIN(order_time)
聚合函数与 GROUP BY 的配合使用场景
分组统计的核心是聚合函数。常见组合包括计数、求和、平均、最值等,但要注意语义是否合理。
COUNT(*)统计每组行数;
COUNT(column)忽略该列 NULL 值再计数
SUM(amount)对数值列求和,但若
amount是字符串(如 '100'),MySQL 会隐式转为数字,可能出错或截断
AVG(price)自动跳过 NULL,但若全为 NULL,结果为 NULL —— 不是 0 慎用
GROUP_CONCAT(name)拼接字符串,默认长度限制 1024,超长会被截断,可通过
SET group_concat_max_len = 1000000临时调整
WHERE 和 HAVING 的区别与误用陷阱
WHERE过滤的是分组前的原始行,
HAVING过滤的是分组后的聚合结果。顺序不可颠倒,且
HAVING只能引用
SELECT中的聚合结果或分组字段。 查订单数 > 5 的用户:
SELECT user_id, COUNT(*) c FROM orders GROUP BY user_id HAVING c > 5不能写成
WHERE COUNT(*) > 5—— 会报错,因为
COUNT(*)在
WHERE阶段还不存在 想先筛掉测试数据再分组?用
WHERE status != 'test'放在
GROUP BY前
HAVING性能通常比
WHERE差,因为它作用于已分组结果,应优先用
WHERE尽量减少输入行数
GROUP BY 多字段与 NULL 值的特殊行为
多字段分组时,MySQL 把所有字段组合视为一个分组键;而
NULL在分组中被视为“相同值”,即所有
NULL会归入同一组 —— 这点容易被忽略。
GROUP BY region, city:(‘CN’, ‘Beijing’) 和 (‘US’, ‘NYC’) 是不同组;(NULL, ‘Shanghai’) 和 (NULL, ‘Guangzhou’) 会分到两个组,但 (NULL, NULL) 和 (NULL, NULL) 会合并 如果某字段允许 NULL,又想把 NULL 单独当一类处理,可显式转换:
GROUP BY IFNULL(category, 'unknown')注意排序:MySQL 8.0+ 默认不保证
GROUP BY结果顺序,需显式加
ORDER BY;老版本可能按分组字段隐式排序,但不可依赖
SELECT IFNULL(dept, 'unassigned') AS dept_group, COUNT(*) AS emp_count, AVG(salary) AS avg_salary FROM employees WHERE hire_date >= '2020-01-01' GROUP BY dept_group HAVING emp_count >= 3 ORDER BY avg_salary DESC;分组统计真正难的不是语法,而是想清楚“你到底要按什么逻辑归类”以及“每个组里你想提取什么代表性信息”。字段是否允许 NULL、聚合函数是否覆盖边界情况、WHERE/HAVING 的执行阶段差异——这些地方一不留神,结果就偏了。
