周测:JOIN 陷阱专项

月报交付。复盘这一周踩过的所有 JOIN 坑,来一场限时专项测评。

学 10 min
练 70 min
盘 40 min
共 120 分钟

复盘 · 10 min

  1. 01回看本周错题,重点看 JOIN 类

专项 12 题 · 110 min

  1. JOIN 之后 count 变多了,列出三种可能原因
    参考答案

    三种原因:① 一对多放大--连了 order_items 这类明细表,行数由「多」的一端决定;② 连接条件漏写或恒真(如 user_id = user_id),退化成笛卡尔积,行数 ≈ 两表行数之积;③ 右表连接列不唯一,本该一对一的关系实际是一对多。排查顺序:先笔算期望行数,再看 EXPLAIN 的估行数。

  2. 一对多连接导致订单金额被重复累加,写出两种修复方案
    参考答案

    方案一:先聚合再连接--CTE/子查询里按 order_id 把明细 sum 成「每单一行」,再 join 回 orders 做聚合;方案二:不连明细,直接用 orders.total_amount(seed 的 §B④ 已把它对齐成明细之和)。口述要点:重复累加的根源是「左表的列被多端的行重复携带」,先压缩多端就消掉了。

  3. 用三种写法查「没有下过单的用户」并对比计划
    参考答案

    三种写法:LEFT JOIN + o.id IS NULL(Hash Left Join,不用去重,大数据量通常最快);NOT EXISTS(优化器常改写成 anti join,计划和 LEFT JOIN 版几乎一样);EXCEPT(两边先去重再做集合减法)。EXPLAIN 对比三种节点,结论写进 notes.md。

  4. LEFT JOIN 后 count(b.id) 与 count(*) 结果不同,解释原因
    参考答案

    LEFT JOIN 匹配不上时左行保留、右侧全 NULL:count(*) 数行,NULL 行也算 1;count(右表.非空列) 跳过 NULL。差值 = 没匹配上的左行数,「没下单显示 0」就是靠它实现的。

  5. 剩余 8 题:LeetCode 中等难度多表题
    参考答案

    选题集中在 INNER/LEFT JOIN、多表 GROUP BY、HAVING--正是本周标「高频」的内容。限时内做完;错题沿用第一周的归因法分三类(没懂概念 / 记不住语法 / 看错题意)记进 mistakes.md。

过关标准 12 题正确 ≥ 9 题;能画图解释一对多连接的金额翻倍问题。

今日复盘

  1. GMV 三个口径对账:全部订单 / 只算已支付 / 已支付且剔除负金额
    参考答案

    月报第一件事是把口径钉死:同一个「GMV」,三种算法三个数--全部订单(含未支付、已取消)、只算已支付、再把 D01 埋的负金额压成 0。三列并排摆出来,差额来自哪一眼就看见。用 LEFT JOIN 是因为 city 可能为空,别让 INNER 把订单吃掉。选哪个口径都算对,错的是不把选择写进报表备注。

    select coalesce(u.city, '未知') as 城市,
           sum(o.total_amount) as 全部口径,
           sum(o.total_amount) filter (where o.status = 2) as 已支付口径,
           sum(greatest(o.total_amount, 0)) filter (where o.status = 2) as 剔负口径
    from orders o
    left join users u on u.id = o.user_id
    group by 1
    order by 已支付口径 desc nulls last;
  2. 按「城市 × 状态」双维度统计订单数与 GMV
    参考答案

    粒度 = GROUP BY 列的全体:一行代表「某城市的某状态」。city 为 NULL、status 为 NULL 的行各自成组,这是口径的一部分,不是脏输出。

    select u.city, o.status,
           count(*) as 单数,
           sum(o.total_amount) as gmv
    from orders o
    join users u on o.user_id = u.id
    group by u.city, o.status
    order by u.city nulls last, o.status;
  3. 翻一遍本周 notes.md,标出还讲不利索的点