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

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

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

相关推荐