GROUP BY 先完成分组再聚合,无索引时需临时表和文件排序;SELECT 中非分组非聚合列在 only_full_group_by 下报错;COUNT(*) 统计所有行,COUNT(col) 仅非 NULL 行;HAVING 在分组后过滤且不能用索引。

GROUP BY 是先分组再聚合,不是边扫边算
MySQL 执行 GROUP BY 时,并非逐行读取就立即调用 COUNT() 或 SUM() 累加。它必须先完成完整的分组划分,才能对每个组独立计算聚合结果。这意味着:如果没索引支撑,MySQL 往往要走临时表 + 文件排序(Using temporary; Using filesort),尤其在大表上会明显变慢。
常见错误现象:EXPLAIN 显示 Extra 列含 Using temporary,且查询响应时间随数据量非线性增长。
- 分组字段无索引 → 强制全表扫描 + 内存/磁盘临时表
- SELECT 中出现未分组也未聚合的列(如
SELECT name, COUNT(*) FROM t GROUP BY dept)→ 在sql_mode=only_full_group_by下直接报错ERROR 1055 -
聚合函数参数含表达式(如
SUM(price * qty))→ 不影响分组逻辑,但可能拖慢单行计算速度
聚合函数对 NULL 的处理是默认忽略,不是报错或转 0
COUNT()、SUM()、AVG() 这些函数天然跳过 NULL 值,这是 SQL 标准行为,不是 MySQL 特有。比如 COUNT(col) 只统计非 NULL 行;SUM(col) 对全 NULL 组返回 NULL,不是 0。
容易踩的坑:
-
COUNT(*)和COUNT(col)结果不同:前者统计所有行,后者只计非空值 -
AVG(col)在整数列上返回DECIMAL类型,可能引发应用层类型不匹配(如 Go 的int64接收失败) - 想把空组补成 0?得靠
COALESCE(SUM(col), 0),不能依赖聚合函数自己“兜底”
GROUP BY 后 HAVING 比 WHERE 更晚执行,且能引用聚合结果
WHERE 过滤发生在分组前,操作的是原始行;HAVING 过滤发生在分组后,操作的是每组的聚合结果。所以 HAVING 能写 HAVING COUNT(*) > 10,而 WHERE 写这个会报错。
性能提示:
齐博B2B系统是一款基于PHP程序和Mysql数据库为基础的开源B2B行业门户电子商务网站建站系统, 系统代码完整、开源,功能全面,架构优秀,提供良好的用户体验、及管理平台,是目前搭建B2B行业门户网站最好的程序之一。齐博B2B具有的功能特点包括:通行证整合功能通过通行证的整合,可以与流行的PHPWIND论坛或Discuz论坛以及Ucenter中心等进行通讯,从而为用户提供更多的交流场所,增加网站
- 尽量把过滤条件往前推——能用
WHERE就别用HAVING,减少参与分组的行数 -
HAVING条件无法利用索引,纯内存过滤,大数据量下慎用复杂表达式 - 若
HAVING中用了非聚合字段(如HAVING dept = 'tech'),MySQL 会允许(依赖sql_mode),但语义模糊,应避免
ORDER BY 与 GROUP BY 共存时,排序字段必须在 SELECT 列表中或满足函数依赖
当语句同时含 GROUP BY 和 ORDER BY,比如:
SELECT dept, COUNT(*) c FROM emp GROUP BY dept ORDER BY c DESC;
这条合法,因为 c 是 SELECT 列表中的别名,且由聚合生成;但下面这句在 only_full_group_by 下会失败:
SELECT dept, COUNT(*) FROM emp GROUP BY dept ORDER BY name;
因为 name 既没出现在 GROUP BY,也不是聚合结果,MySQL 无法确定每组该取哪个 name 来排序。
解决方式只有两个:
- 把
name加进GROUP BY(改变分组粒度) - 用聚合函数包裹,如
ORDER BY MAX(name)
函数依赖规则(MySQL 5.7+)允许某些情况绕过限制,比如 dept 是主键,name 是其依赖列,但这种隐式行为难维护,不建议依赖。









