WEEK 03 0 / 7 天
需求变绕了:子查询、CTE 与章法
商品越上越多,公司上线了类目体系(categories 表进场);运营的需求也从「统计一下」变成了「找出买过 A 又没买过 B 的用户」这种绕来绕去的活。你开始需要把复杂需求拆开、有章法地写的本事。
目标:拿到一个绕的需求,能拆成几步、有章法地写出来,而不是硬凑。
剧情 商品要归类了。类目是「电子 > 手机 > 配件」这样的树,存在 parent_id 里。第一个需求:按一级类目看销售。
学 · 40 min
练 · 65 min
- 跑 seed.sql「W3」段:建 categories 并回填 products.category_id
- 自连接输出「一级类目名 | 二级类目名」全表
- 按一级类目统计 GMV(order_items -> products -> categories 连两次)
- 给定一个二级类目,查出它的父类目
- 想想:为什么 products 不直接冗余一级类目 id?写两句利弊进 notes.md
过关按一级类目的 GMV 报表跑通,连接链能画出来。
剧情 老板:「找出消费高于平均水平的那批人,我给他们发券。」平均值本身就要一条查询来算--你第一次需要查询里套查询。
练 · 70 min
- 用标量子查询输出「每个用户消费额 − 全站平均消费额」
- 用派生表先按用户聚合订单数,再连接 users 输出
- 用
WHERE id IN (子查询)查有过已支付记录的订单 - 写一个返回多行的标量子查询,记录报错信息
- 同一需求分别用派生表和 CTE 写,对比可读性
- 月报续集:商品 GMV 的 TOP10 及其占总 GMV 的比例(D13 缺的工具今天到货)
- 月报续集:复购用户数(下单 ≥ 2 次的用户)及复购率
过关能说出标量子查询在什么情况下会被执行 N 次(相关子查询)。
剧情 同行群里的事故通报:有人用
NOT IN 子查询查「没下过单的用户」,上线后返回空集,被运营当成故障投诉。你要彻底搞懂,别踩同一个坑。 学 · 45 min
练 · 60 min
- 用 IN、EXISTS、JOIN 三种写法查「下过单的用户」,对比 EXPLAIN
- 在子查询列里制造 NULL,复现
NOT IN返回空集 - 把第 2 题改成
NOT EXISTS,验证结果正确 - 再用
LEFT JOIN ... WHERE b.id IS NULL写第三遍 - 在 12 万行明细上跑三种反连接写法,做一张耗时对比表
过关手上有三写法耗时对比表;能完整讲出 NOT IN 遇 NULL 的推导过程。
剧情 老板在类目管理页点了「电子」,想看它下面所有层级类目的汇总销售。层级不固定,普通 JOIN 搞不定。
练 · 60 min
- 查每个分类的完整路径(如「电子 > 手机 > 配件」)
- 给定「电子」,查它的全部子孙分类及其销售汇总
- 用递归 CTE 生成 2026 全年日期序列(不用 generate_series)
- 算出每个分类所在的层级深度
- 自建 employees 表做组织架构下钻,输出每人的汇报链
过关不看资料写出一个递归 CTE,并逐段解释初始项和递归项各做什么。
剧情 运营的原话:「近 3 个月,每月新客的 GMV 和老客的 GMV 分开算。」你决定以后拿到需求先拆步骤再动手,用 CTE 把每一步写成能读的段落。
学 · 40 min
练 · 65 min
- D13 的最后一笔欠账:「每月新增用户数与当月下单用户数」--两段各自聚合,再对齐成一张表
- 写一个三级 CTE:清洗 -> 聚合 -> 排名
- 同一查询加与不加
MATERIALIZED,对比执行计划差异 - 「近 3 个月每月新客 GMV 与老客 GMV」:先写五步拆解,再写 SQL
- 写一个被引用两次的 CTE,观察是否被计算两次
过关能说清 PG 里 CTE 什么时候会被物化、物化意味着什么代价。
剧情 运营要做「每个类目热销 TOP3」看板。你上次用整表聚合再过滤的写法,数据一大就慢;这次学个新武器。
学 · 40 min
练 · 65 min
- 用 LATERAL 查每个用户最近 3 笔订单
- 用 LATERAL 查每个一级类目销量前 3 的商品
- 用
ROW_NUMBER把第 2 题再写一遍(预习下周窗口函数语法) - 用
DISTINCT ON写 N=1 的版本 - 三种写法都跑
EXPLAIN ANALYZE,记录耗时做成对比表
过关能说出 LATERAL 在「分组多、每组取少量」场景下为什么可能更快。
剧情 三周了。本周测评改成白板模式--纸上手写、不许运行,模拟面试现场。
复盘 · 10 min
测评 · 110 min
- 60 分钟纸上手写 6 题(不许运行、不许查文档)--模拟白板环节
- 上机逐题验证,统计有几题一次通过
- 把写错的地方标红,归入 mistakes.md
- 对着镜子讲一遍其中最难那题的解题思路
过关6 题中至少 4 题手写版本能直接跑通。