group by后count/sum虚高是因为join先将1行扩展为n行,导致分组前数据膨胀;应使用count(distinct column)去重或先聚合再join来避免。

为什么 GROUP BY 后 COUNT/SUM 还是虚高?
不是 GROUP BY 写错了,而是 JOIN 先把 1 行撑成了 N 行——比如用户表 1 行关联订单表 3 行、地址表 2 行,JOIN 后就是 6 行;GROUP BY user_id 是在 6 行上分组,COUNT(*) 自然变成 6。
此时你要统计“该用户有几个地址”,答案应该是 2,但直接 COUNT(*) 给你报 6。
用 COUNT(DISTINCT column) 修正被放大的计数
当目标是统计被 JOIN 放大的维度(如订单数、地址数、标签数),必须显式去重:
-
COUNT(DISTINCT o.order_id)→ 正确得到每个用户的订单数量,哪怕订单和地址做了笛卡尔积 -
COUNT(DISTINCT a.addr_id)→ 正确得到地址数,不受订单行数干扰 - 不能写
COUNT(DISTINCT *)—— 语法错误;字段必须明确,且来自被 JOIN 的表,并能唯一标识业务实体 - 注意
NULL:如果addr_id允许为NULL,COUNT(DISTINCT addr_id)会自动忽略它;若需包含空值逻辑,得先用COALESCE(addr_id, -1)
先聚合再 JOIN:避免行数爆炸的稳妥做法
比起一次性 JOIN 所有表再 GROUP BY,更稳的方式是“各自聚合,再关联”:
- 先查每个用户的订单总数:
(SELECT user_id, COUNT(*) AS order_cnt FROM orders GROUP BY user_id) - 再查每个用户的地址数:
(SELECT user_id, COUNT(*) AS addr_cnt FROM addresses GROUP BY user_id) - 最后
LEFT JOIN这两个子查询到users表——中间无行数爆炸风险 - 优势:逻辑清晰、执行计划可控、避免 MySQL 的
only_full_group_by报错(尤其在SELECT多个非分组字段时) - 缺点:子查询可能无法利用外层
WHERE条件下推,大数据量时注意给orders(user_id)等字段加索引
GROUP BY 字段必须覆盖语义主键
如果你 JOIN 了三张表,又希望按用户维度统计,但 SELECT 里还带了 order_status 或 addr_type,GROUP BY 就不能只写 user_id:
- 写
GROUP BY user_id, order_status→ 实际是按“用户+订单状态”分组,每组统计的是该状态下的订单数,不是用户总数 - 写
GROUP BY user_id却SELECT order_status→ 在 strict 模式下直接报错:Expression #2not in GROUP BY - 如果真要带
order_status,要么把它加进GROUP BY,要么用聚合函数包裹,比如MAX(order_status)或STRING_AGG(DISTINCT order_status, ',')
SELECT * 看几行原始数据,比猜逻辑更快。











