group by 是按字段值归组并聚合,非聚合字段须在 group by 中或用聚合函数包裹;where 筛分组前数据,having 筛分组后结果;多字段 group by 基于联合键;count(*) 统计行数,count(字段) 忽略 null。

GROUP BY 不是“排序”或“去重”,而是按字段值把行归成组,再对每组算一个结果;不配聚合函数就执行失败,或者返回不可靠的单行值。
SELECT 中非聚合字段必须出现在 GROUP BY 里
MySQL 5.7+ 默认启用 sql_mode=only_full_group_by,如果写 SELECT user_id, product_name, COUNT(*) FROM orders GROUP BY user_id,会直接报错:Expression #2 of SELECT list is not in GROUP BY clause。
- 错误原因:同一
user_id可能对应多个product_name,MySQL 不知道该取哪一个 - 正确写法:要么把
product_name加进GROUP BY(变成多维分组),要么用聚合函数包裹,比如GROUP_CONCAT(product_name)或MAX(product_name) - 临时绕过?不建议。改
sql_mode会让结果依赖执行引擎随机选值,线上环境极易出错
WHERE 和 HAVING 别混用
WHERE status = 1 是在分组前筛原始行;HAVING COUNT(*) > 5 是在分组后筛组。顺序错了,结果就全偏了。
- 想查“已支付且订单数超 3 的用户”:用
WHERE status = 1 GROUP BY user_id HAVING COUNT(*) > 3 - 想查“所有用户中,订单总金额超 10000 的人”:用
GROUP BY user_id HAVING SUM(amount) > 10000;不能写成WHERE SUM(amount) > 10000——SUM()在WHERE阶段还没计算 -
HAVING支持别名,比如HAVING total_amount > 10000(前提是SELECT里定义了SUM(amount) AS total_amount)
多字段 GROUP BY 实际是联合键分组
GROUP BY city, payment_method 不是“先按 city 分、再在每个 city 里按 payment_method 分”,而是把 (city, payment_method) 当作一个整体键来匹配,比如 ('上海', 'alipay') 和 ('上海', 'wechat') 是两个完全独立的组。
- 字段顺序不影响分组逻辑,但影响默认排序(没写
ORDER BY时,结果可能按city升序、再按payment_method升序) - 如果要统计“每个城市销量最高的支付方式”,不能靠
GROUP BY city, payment_method ORDER BY SUM(sales) DESC LIMIT 1——那只是全局 Top 1,得用窗口函数ROW_NUMBER() OVER (PARTITION BY city ORDER BY SUM(sales) DESC) -
COUNT(*)统计本组行数;COUNT(amount)会跳过amount为NULL的行,二者语义不同
聚合函数的行为细节容易被忽略
AVG()、SUM()、COUNT() 看似简单,但 NULL 处理和空组行为常引发偏差。
-
AVG(amount)自动忽略amount IS NULL的行,分母是该组非 NULL 行数,不是总行数 - 某组全为
NULL时,AVG()返回NULL,SUM()也返回NULL,但COUNT(*)仍返回实际行数(哪怕所有字段都是NULL) - 需要判断“该组是否有数据”?用
COUNT(*) > 0,不要写IFNULL(COUNT(*), 0) > 0——COUNT(*)永远不为NULL - 想补全缺失组合(比如某城市没有“bank_transfer”记录,也要显示为 0)?
GROUP BY本身做不到,得靠LEFT JOIN预生成维度表











