mysql执行SQL时group by在哪个阶段处理_聚合流程说明

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

GROUP BY 在 SQL 执行流程中的位置

MySQL 执行一条含

GROUP BY
的查询时,
GROUP BY
不是“先过滤再分组”,也不是“最后才分组”,而是发生在
WHERE
之后、
HAVING
之前、
SELECT
列计算之前的关键阶段。它属于逻辑执行顺序中「分组聚合」这一环节,直接影响后续所有聚合函数(如
COUNT()
SUM()
)的输入数据集。

标准逻辑执行顺序与 GROUP BY 的实际作用点

虽然 MySQL 物理执行可能因优化器重排(比如下推条件),但语义上遵循如下逻辑顺序:

FROM
JOIN
→ 确定原始数据源
WHERE
→ 过滤行(此时还无分组概念)
GROUP BY
→ 将满足
WHERE
的结果按指定列/表达式划分为多个组,每组产生一行中间结果
HAVING
→ 对每个组的聚合结果进行过滤(可引用
GROUP BY
列和聚合函数)
SELECT
→ 计算最终输出列(非聚合列必须出现在
GROUP BY
中,否则报错
sql_mode=only_full_group_by
ORDER BY
/
LIMIT
→ 最后排序或截断

注意:

GROUP BY
后每一行代表一个组,原始表中多行被压缩为一行;后续所有聚合函数都在这个“每组一行”的上下文中运行。

常见误解与典型错误场景

很多问题源于混淆了“行级操作”和“组级操作”的边界:

WHERE
子句里写聚合函数(如
WHERE COUNT(*) > 1
)会报错——因为
WHERE
执行时还没分组,
COUNT(*)
无意义
把本该放
HAVING
的条件误写进
WHERE
(例如想查“订单数 > 5 的用户”,却写成
WHERE COUNT(order_id) > 5
开启
ONLY_FULL_GROUP_BY
模式后,
SELECT
中出现未在
GROUP BY
中声明的非聚合字段(如
SELECT user_id, name FROM orders GROUP BY user_id
),MySQL 会拒绝执行
GROUP BY
字段使用函数但未在
SELECT
HAVING
中保持一致(如
GROUP BY DATE(created_at)
,却在
SELECT
中写
created_at

简单验证流程的 SQL 示例

用以下语句可以直观看到各阶段效果:

SELECT user_id, COUNT(*) AS cnt
FROM orders 
WHERE status = 'paid'
GROUP BY user_id
HAVING COUNT(*) >= 3
ORDER BY cnt DESC;

执行过程分解:

WHERE status = 'paid'
:先筛出已支付订单
GROUP BY user_id
:将这些订单按用户归并,每个
user_id
成为一个组
COUNT(*)
在每个组内统计行数,生成临时列
cnt
HAVING COUNT(*) >= 3
:只保留订单数 ≥ 3 的用户组
SELECT
输出
user_id
cnt
ORDER BY
再排序

真正容易被忽略的是:一旦进入

GROUP BY
阶段,原始行就不可见了。所有后续操作都只能基于“组”而非“行”。如果你需要组内明细,得用子查询、窗口函数或者
GROUP_CONCAT()
这类特殊聚合来绕过限制。

相关推荐