mysql中GROUP BY子句分组统计的实现方法

来源:这里教程网 时间:2026-02-28 20:43:56 作者:

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 的执行阶段差异——这些地方一不留神,结果就偏了。

相关推荐