执行计划中rows和type字段是判断先聚合后关联是否生效的唯一依据;需确保聚合子查询只选必要字段、ON条件严格对齐分组键、避免SELECT;LEFT JOIN中COUNT()与COUNT(列)语义不同;一对多关联必须拆分聚合子查询以防统计翻倍。

直接看执行计划里 rows 和 type 字段
别靠猜,EXPLAIN 是唯一可靠依据。先聚合后关联是否真生效,关键看两处:rows 是否显著下降、type 是否为 ref 或 range 而非 ALL。如果子查询的 rows 还是几百万,说明聚合没压住数据量,可能漏了 WHERE 下推或索引缺失。
写法上必须显式隔离聚合逻辑
不能只靠括号套一层就完事。真正起效的写法要满足三点:
- 聚合子查询只
SELECT必需字段,例如SELECT user_id, SUM(amount) AS total FROM orders WHERE created_at >= '2026-09-01' GROUP BY user_id - 外层
JOIN的ON条件必须严格对齐子查询的分组键,比如u.id = t.user_id,不能错写成u.user_id = t.user_id(字段名不一致) - 聚合子查询里避免
SELECT *或冗余字段,否则内存和网络开销白增
LEFT JOIN 场景下 COUNT(*) 和 COUNT(列) 行为差异极大
这是最容易被忽略却直接影响结果正确性的点。在先聚合再关联的 LEFT JOIN 中:
-
COUNT(*)统计的是左表行数,哪怕右表聚合结果为NULL,它也返回1 -
COUNT(t.total)或COUNT(t.user_id)才会跳过NULL,真正反映“有订单的用户数” - 若字段允许
NULL(如order_amount),用COUNT(t.order_amount)可能漏掉零金额订单;稳妥做法是用主键COUNT(t.user_id)
一对多关联时,不先聚合会导致 SUM/COUNT 翻倍
这不是慢的问题,是错的问题。比如一个用户有 3 笔订单、2 个标签,直接 JOIN orders 再 JOIN tags,该用户会生成 6 行。后续 SUM(amount) 就是真实值的 2 倍,COUNT(*) 是真实订单数的 2 倍——GROUP BY user_id 救不回来,因为重复行已在 JOIN 阶段固化。
必须拆成两个独立聚合子查询:SELECT user_id, SUM(amount) FROM orders GROUP BY user_id 和 SELECT user_id, STRING_AGG(tag_name, ',') FROM user_tags GROUP BY user_id,再用 user_id 对齐 LEFT JOIN。


















