mysql中GROUP BY与聚合函数的执行过程

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

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
写这个会报错。

性能提示:

尽量把过滤条件往前推——能用
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
是其依赖列,但这种隐式行为难维护,不建议依赖。

相关推荐