GROUP BY本身不导致膨胀,膨胀发生在JOIN阶段;它仅对已膨胀的中间结果统计,故SUM虚高。执行顺序为FROM→JOIN→WHERE→GROUP BY→SELECT,EXPLAIN中rows突增或出现Nested Loop/Using join buffer即为膨胀信号,根治需子查询预聚合或修正ON条件。

GROUP BY 本身不会导致数据膨胀,膨胀发生在它之前的 JOIN 阶段;GROUP BY 只是忠实地对已经膨胀的中间结果做统计,所以 COUNT/SUM 看起来“翻倍”了。
为什么 EXPLAIN 显示行数正常,但 SUM 却虚高?
执行顺序是 FROM → JOIN → WHERE → GROUP BY → SELECT,膨胀发生在 JOIN 后、GROUP BY 前。你看到的 GROUP BY 输出行数可能完全正确(比如按 user_id 分组得到 1 万行),但每一行对应的 SUM(amount) 已被重复计算了 N 次——因为 JOIN 把 1 行订单扩成了 N 行明细。
- 查
EXPLAIN的rows列:如果orders表预估 1 万行,但JOIN order_items后突然变成 50 万行,就是膨胀信号 - PostgreSQL 中看到
Nested Loop (Join Filter: true),MySQL 中看到type: ALL+Extra: Using join buffer,基本等于漏写了ON条件 -
COUNT(DISTINCT order_id)正常,但SUM(item_price)异常偏大,说明明细层已参与 JOIN 并放大
LEFT JOIN 中 WHERE 和 ON 放错位置,等于主动制造膨胀
这不是风格问题,是执行逻辑的根本差异:放 WHERE 是先全量配对再过滤;放 ON 是从源头控制右表参与连接的行数。
- 错误写法:
LEFT JOIN customers c ON o.customer_id = c.id WHERE c.status = 'active'→ 先把所有客户(含status IS NULL)都 JOIN 进来,再踢掉非 active 行,中间已膨胀 - 正确写法:
LEFT JOIN customers c ON o.customer_id = c.id AND c.status = 'active'→ 右表只拉status = 'active'的记录,没膨胀 - 如果业务要求保留无客户匹配的订单,又只取活跃客户信息,
WHERE绝对不能碰右表字段,否则LEFT JOIN就退化成INNER JOIN
子查询预聚合才是根治方案,不是加 DISTINCT 能糊弄过去的
DISTINCT 对 SUM 无效:SUM(DISTINCT amount) 是去重金额值,不是去重行。真正要做的,是让多端表先“压缩”成每关联键一行,再和主表拼接。
- 对
order_items:必须写成(SELECT order_id, SUM(price) AS total_price FROM order_items GROUP BY order_id),不能留明细字段直接连 - 子查询外层别加
ORDER BY或LIMIT——优化器可能无法下推,导致全表扫 - 过滤条件(如
status = 'paid')必须写在子查询里,不能挪到外层WHERE,否则子查询输出仍含无效行 - 如果右表本身有重复(如历史客户表含多条同
id记录),光靠ON不够,必须先子查询去重或取最新一条
窗口函数能绕开部分 JOIN 膨胀,但有硬性限制
当你要“每个订单的最新支付时间 + 所有明细总金额”,传统三表 JOIN 容易双重膨胀。窗口函数可分层计算,不互相干扰。
- 示例中
FIRST_VALUE(p.paid_at) OVER (PARTITION BY p.order_id ...)和SUM(oi.price) OVER (PARTITION BY oi.order_id)各自独立运行,不产生交叉 -
LATERAL子查询确保每条订单只取一条支付记录,从源头掐断膨胀 - 窗口函数必须写在
SELECT中,不能出现在WHERE或ON条件里——否则报错invalid reference to FROM-clause entry -
SELECT order_id, SUM(amount), AVG(amount) OVER (PARTITION BY status)在 MySQL 8.0 / PostgreSQL / SQL Server 中会直接报错:聚合函数和窗口函数不能混用在同一级SELECT
最容易被忽略的是:你以为问题出在 GROUP BY 写法,其实根源在 JOIN 的物理执行路径上——而这个路径,往往由 ON 条件是否完整、子查询是否提前压缩、以及右表是否存在隐式重复共同决定。

















