GROUP BY 必须配合聚合函数使用,非分组字段需用COUNT、AVG、MAX等聚合;WHERE在分组前过滤行,HAVING在分组后过滤组;多字段分组时注意NULL处理和索引顺序一致性。

GROUP BY 语句必须配合聚合函数使用
直接写 SELECT name FROM users GROUP BY dept_id 会报错(尤其在 MySQL 8.0+ 严格模式下),因为非分组字段 name 没有明确的聚合逻辑。MySQL 不知道该取哪条记录的 name —— 是第一条?最大值?还是随便一个?
正确做法是把非分组字段用聚合函数包裹,比如:SELECT dept_id, COUNT(*), AVG(salary), MAX(age) FROM users GROUP BY dept_id。这里 COUNT、AVG、MAX 就是明确告诉 MySQL “我要这个分组里的总数、平均薪资、最大年龄”。
- 常用聚合函数:
COUNT()、SUM()、AVG()、MIN()、MAX()、GROUP_CONCAT() -
GROUP_CONCAT(name)能把同组的姓名拼成字符串,适合查看成员列表 - 别名建议加:
GROUP_CONCAT(name) AS members,避免结果列无名
WHERE 和 HAVING 的区别不能搞混
WHERE 过滤的是“行”,在分组前执行;HAVING 过滤的是“组”,在分组后执行。想查“人数超过 5 人的部门”,必须用 HAVING COUNT(*) > 5,而不是 WHERE COUNT(*) > 5——后者语法错误,因为 COUNT 在 WHERE 阶段还不存在。
- 错误示例:
SELECT dept_id, COUNT(*) FROM users WHERE COUNT(*) > 5 GROUP BY dept_id→ 报错Invalid use of group function - 正确写法:
SELECT dept_id, COUNT(*) FROM users GROUP BY dept_id HAVING COUNT(*) > 5 - 如果还要按入职时间筛选,先
WHERE hire_date >= '2022-01-01',再分组,最后HAVING
多字段分组要注意字段顺序和 NULL 处理
写 GROUP BY dept_id, status 和 GROUP BY status, dept_id 结果一样,但排序行为可能不同(尤其搭配 ORDER BY 时)。更关键的是:NULL 值会被当作独立一组处理,且多个 NULL 会归到同一组。
- 如果
status允许为 NULL,GROUP BY dept_id, status会单独产生一行dept_id=1, status=NULL - 想把 NULL 当作普通值统一标记,可提前转换:
GROUP BY dept_id, COALESCE(status, 'unknown') - 避免在分组字段上用函数(如
GROUP BY YEAR(create_time)),会导致索引失效,大数据量时很慢
性能差?先看执行计划,再考虑加索引
分组统计慢,90% 是因为没走索引。用 EXPLAIN SELECT dept_id, COUNT(*) FROM users GROUP BY dept_id 查看 type 是否为 index 或 range,key 是否命中索引。
- 理想索引:
ALTER TABLE users ADD INDEX idx_dept_status (dept_id, status)—— 多字段分组时,索引顺序要和GROUP BY字段顺序一致 - 如果只按
status分组,但索引是(dept_id, status),则无法利用该索引的前导列,效果打折 - 临时表(
Using temporary)和文件排序(Using filesort)同时出现,基本意味着需要优化索引或减少分组粒度


















