mysql中GROUP BY语句分组统计的基本用法

9次阅读

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

mysql中GROUP BY语句分组统计的基本用法

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 逻辑

比如表 ordersuser_idproduct_idamount 三列,执行 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
  • 性能上,先 WHEREGROUP BYHAVING 是最优顺序,避免对无意义的数据分组

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) 就毫无意义。分组逻辑必须从业务目标出发,而不是语法能跑通就完事。

text=ZqhQzanResources