← [ BACK_TO_NOTES ]

SQL JOIN、GROUP BY 与聚合查询

串联 JOIN、GROUP BY 与聚合函数:先组合关系,再确定统计粒度,最后对每组计算,并处理 NULL、过滤阶段和行数放大。

#synthesis #growing #backend #sql #database

[!info] related notes

SQL JOIN、GROUP BY 与聚合查询

核心链路

很多业务查询都可以先按三步思考:

JOIN
→ 把需要的事实组合到同一结果粒度

GROUP BY
→ 明确要“按谁”统计

Aggregate
→ 对每一组算 COUNT / SUM / AVG / MIN / MAX

例如“所有用户及其订单数,包括 0 单用户”:

SELECT
  u.id,
  u.name,
  COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o
  ON o.user_id = u.id
GROUP BY u.id, u.name;

为什么顺序重要

先 JOIN

LEFT JOIN 保留所有 User,并把无订单用户的订单列补成 NULL。

再 GROUP BY

一个用户匹配多条订单后会出现多行,因此必须明确“按 User”形成统计组。

最后 COUNT

这里使用:

COUNT(o.id)

而不是 COUNT(*)。无订单用户虽然仍有一条 LEFT JOIN 结果行,但 o.id = NULL,所以 COUNT(o.id) = 0。

过滤到底放哪一层

逻辑心智模型:

FROM / JOIN
→ ON      控制匹配
→ WHERE   过滤参与分组的行
→ GROUP BY
→ Aggregate
→ HAVING  过滤聚合后的组
→ SELECT / ORDER BY

因此:

  • “只匹配 published Article,但仍保留没有 published Article 的 User”——右表条件常放 ON;
  • “只统计 paid Order”——放 WHERE;
  • “只保留订单数 >= 3 的 User”——放 HAVING。

最大的正确性风险:粒度被 JOIN 放大

如果同时 JOIN 两个 1:N:

User
├─ 3 Orders
└─ 4 Tags

直接把两边一起 JOIN 可能产生 3 × 4 = 12 行,从而把 COUNT、SUM 重复放大。

所以复杂统计前应明确:

当前结果集“一行代表什么”?

必要时先各自聚合、用子查询/CTE,或拆成批量查询,而不是盲目在一个大 JOIN 上直接 SUM。

性能边界

JOIN、GROUP BY 本身不是“慢查询”的同义词。性能取决于:

  • 表规模和过滤选择性;
  • JOIN 键和过滤列是否有合适索引;
  • 统计信息是否准确;
  • 是否产生巨大中间结果;
  • 优化器选择的 Join / Aggregate 算法。

需要判断真实代价时,应继续看执行计划,而不是只凭 SQL 语法猜性能。

最短记忆

JOIN      → 表怎么组合
GROUP BY  → 按什么粒度分组
Aggregate → 每组算什么
WHERE     → 哪些行参与
HAVING    → 哪些组留下
创建于 2026/5/7 更新于 2026/9/14
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本