GROUP BY 必须与聚合函数配合使用或确保SELECT列全在GROUP BY中,否则MySQL 5.7+默认报错;HAVING用于分组后过滤,WHERE不能使用聚合函数;NULL值在分组中被视为同一组。

GROUP BY 必须和聚合函数一起用才有效
单独写 SELECT name FROM users GROUP BY name 在 MySQL 5.7+ 默认模式下会报错:Expression #1 of SELECT list is not in GROUP BY clause。这是因为启用了 sql_mode=ONLY_FULL_GROUP_BY,它要求所有 SELECT 列要么是 GROUP BY 的字段,要么包裹在聚合函数里。
- 正确写法:用
COUNT()、SUM()、AVG()等处理非分组字段 - 临时绕过(不推荐):执行
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));,但会影响数据语义一致性 - 业务上真正想查的往往不是“某个 name”,而是“每个 name 对应多少条记录”这类统计结果
SELECT 列必须严格符合 GROUP BY 逻辑
比如表 orders 有 user_id、product_id、amount 三列,执行 SELECT user_id, product_id, SUM(amount) FROM orders GROUP BY user_id 是非法的——因为 product_id 没参与分组,MySQL 不知道该取哪一行的值。
- 合法写法一(按用户汇总):
SELECT user_id, COUNT(*), SUM(amount) FROM orders GROUP BY user_id - 合法写法二(按用户+商品组合汇总):
SELECT user_id, product_id, SUM(amount) FROM orders GROUP BY user_id, product_id - 如果非要查
product_id又只按user_id分组,得用聚合函数包装,比如MAX(product_id)或GROUP_CONCAT(product_id),但要清楚这代表什么语义
HAVING 用来过滤分组后的结果,WHERE 不能替代
WHERE 在分组前过滤行,HAVING 在分组后过滤组。例如“找出下单总额超过 1000 的用户”,必须用 HAVING:
SELECT user_id, SUM(amount) AS total FROM orders GROUP BY user_id HAVING total > 1000;
- 写成
WHERE SUM(amount) > 1000会直接报错:聚合函数不能出现在WHERE子句中 -
HAVING可以引用SELECT中的别名(如total),但部分旧版 MySQL 要求写完整表达式SUM(amount) > 1000 - 性能上,先
WHERE再GROUP BY再HAVING是最优顺序,避免对无意义的数据分组
NULL 值在 GROUP BY 中会被当作同一组
如果 user_id 允许为 NULL,那么所有 NULL 值会聚合成一组。比如:
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id;
结果里可能出现一行 NULL | 5,表示有 5 条记录的 user_id 是空值。
- 需要区分“未填”和“已填但值为 0”时,建议用确定值(如
0或-1)代替NULL,或加条件WHERE user_id IS NOT NULL - 排序时
NULL默认排最前(ASC)或最后(DESC),但分组行为本身不依赖排序 - 多字段
GROUP BY a, b时,只要任一字段为NULL,就按NULL处理,不会因另一字段不同而拆开
GROUP BY 时,最容易忽略的是字段语义是否真的可聚合——比如对时间字段用 MIN(created_at) 和 MAX(created_at) 很自然,但对用户名用 AVG(name) 就毫无意义。分组逻辑必须从业务目标出发,而不是语法能跑通就完事。










