WHERE在分组前过滤行,HAVING在分组后过滤组;WHERE不可用聚合函数,HAVING可;SELECT非聚合列须在GROUP BY中或被聚合函数包裹;GROUP_CONCAT可汇总明细但有长度限制;GROUP BY性能依赖索引与避免隐式排序。

GROUP BY 后 WHERE 和 HAVING 的区别必须分清
WHERE 在分组前过滤行,HAVING 在分组后过滤组。用错就查不到数据或报错 Unknown column in 'having clause'。
常见错误:把聚合条件(比如 COUNT(*) > 5)写在 WHERE 里——MySQL 会直接拒绝,因为此时还没分组,COUNT(*) 还没意义。
- WHERE 适用于单行条件:
WHERE status = 'active' - HAVING 适用于分组后判断:
HAVING COUNT(*) >= 3 - 如果既要预过滤又要后筛选,两个都得写,顺序固定:先 WHERE,再 GROUP BY,最后 HAVING
SELECT 列必须在 GROUP BY 中出现或被聚合函数包裹
MySQL 5.7+ 默认开启 ONLY_FULL_GROUP_BY 模式,违反这条规则会直接报错:Expression #2 of SELECT list is not in GROUP BY clause...
这不是 MySQL “不讲武德”,而是 SQL 标准要求:非聚合列的值在组内可能有多个,数据库无法确定该返回哪一个。
- 正确写法:
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id - 错误写法:
SELECT user_id, product_name FROM orders GROUP BY user_id(product_name未聚合也未分组) - 想取每组某个具体值?改用聚合函数:
MAX(product_name)、GROUP_CONCAT(product_name),或加子查询/窗口函数
用 GROUP_CONCAT 避免丢失明细信息
单纯 GROUP BY 只能拿到统计值,但业务常需要“每个用户买了哪些商品”这类汇总+展开需求。硬拼 JOIN 容易重复或漏行,GROUP_CONCAT 是更轻量的解法。
- 基础用法:
GROUP_CONCAT(product_name),默认逗号分隔 - 控制格式:
GROUP_CONCAT(DISTINCT product_name ORDER BY product_name SEPARATOR ' | ') - 注意长度限制:
GROUP_CONCAT默认最大 1024 字符,超长会被截断;可通过SET SESSION group_concat_max_len = 10000临时调大 - 它不是万能的:不能替代 JSON 数组,也不支持嵌套结构;真要导出结构化明细,还是得用应用层处理或
JSON_AGG(MySQL 8.0+)
GROUP BY 性能瓶颈常藏在隐式排序和索引缺失里
MySQL 在执行 GROUP BY 时,默认会对分组字段做排序(除非显式加 ORDER BY NULL),这在大数据量下非常拖慢速度。更隐蔽的问题是:没有为 GROUP BY 字段建索引,会导致全表扫描+临时表+文件排序。
- 确认是否用了索引:用
EXPLAIN看type是否为ref/range,Extra是否含Using temporary; Using filesort - 复合查询要注意最左匹配:如果写
GROUP BY category, status,索引必须是(category, status),反过来不行 - 避免在分组字段上用函数:
GROUP BY YEAR(created_at)会失效索引,改用范围查询或冗余年份字段
GROUP BY 看似简单,但聚合逻辑、SQL 模式、索引设计、结果可读性这四块任何一处没对齐,查出来的数据就可能错、慢、缺、乱。

















