PostgreSQL · 从零入职到扛住技术面 · 一条业务主线

八周 SQL 冲刺计划

剧情设定:你零基础入职一家电商创业公司,全公司的数据从老板发来的一张订单表开始。56 天里业务一路长大--运营导数据、增长团队要留存、支付模块上线、订单冲到一百万、大促备战--每个知识点都是被当天的需求逼出来的。每天 2 小时固定时间盒,写明公司发生了什么、你要做什么、做到什么程度算过关。

怎么用

每天 2 小时怎么花

固定配比,不要临时决定。练的时间必须多于看的时间——面试考的是你能不能当场写出来,不是你读过多少。下面是 56 天的平均配比,每天的实际分钟数写在各自的卡片上。

33 min · 学
66 min · 练
21 min · 盘

只看当天「学」栏列出的点,不发散。每点看完用自己的话写一句「它解决什么问题 / 什么时候不该用」,写不出来就是没懂。

「练」栏是当天的业务需求拆成的编号任务,按顺序做,先不看答案。卡住超过 8 分钟再点开题目下方的「参考答案」。每题写完跑一遍 EXPLAIN

对照当天的过关标准自查。没过关的,第二天开头 15 分钟补,不往后拖。错题写进错题本。

  • 跟着剧情走。表是随业务逐张上线的,别跳到还没上线的表和还没发生的需求--提前用未来的表做题,等于把「被需求逼着学」变成了背目录。
  • 三条硬规则。① 不抄答案,写不出来就留空第二天重做;② 每天的「练」栏至少完成前 3 题;③ 每周日的测评不补不拖。
  • 断更处理。漏一天不补课,直接顺延--保持节奏比追进度重要。连漏三天,回到本周第一天重做。
  • 面试导向。标「高频」的是面试官几乎必问的点,这些天的复盘笔记要写成「能直接讲出口」的话术。第 8 周把整个项目变成你的简历故事。
  • 计时器。每天点「开始今天」,三个阶段会按当天的分钟配比自动推进并提示,切到后台也照常走。
Day 0 · 开课前

环境与练习数据集

整个计划都在同一套自建电商库上练,但表不是一天建成的--它们随剧情逐张上线:老板的订单表、运营导来的用户和商品、增长团队的登录日志、支付模块的流水。到面试时,这就是一个你能从头讲到尾的项目经历。

# 起一个 PostgreSQL 16(最省事,不污染本机)
docker run -d --name pg16 -e POSTGRES_PASSWORD=dev -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data postgres:16
docker exec -it pg16 psql -U postgres

CREATE DATABASE shop;
\c shop

-- 练习库表结构(随业务逐张上线,D 号见注释;D01 只建 orders 一张)
orders(id, user_id, status, total_amount, created_at, paid_at)  -- D01 老板发来的订单导出,全课程第一张表
users(id, name, email, city, created_at)  -- D08 运营导来的用户表
products(id, name, category_id, price numeric(10,2), stock, created_at)  -- D09 商品表(category_id 要等 D15 类目上线才填)
order_items(id, order_id, product_id, qty, unit_price)  -- D09 订单明细,连接 orders 和 products 的桥
categories(id, name, parent_id)  -- D15 类目上线,自关联树,递归 CTE 用
user_logins(id, user_id, login_at, ip)  -- D22 增长团队接入的登录日志,连续登录 / 留存题用
payments(id, order_id, method, amount, status, paid_at)  -- D32 支付模块上线的流水表

# psql 必会的 6 个元命令
\l   \dt   \d orders   \di   \x   \timing on

数据分阶段灌入

seed.sql 按剧情分了段,每天上线什么数据跑对应那一段:D01 五万订单(含脏数据)-> W2 用户 / 商品 / 明细 -> W3 类目 -> W4 登录日志 -> W5 支付流水 -> W6 D36 时间快进到百万级。别一口气全灌:后面几周的现象会被提前破坏。

笔记与错题本

两个文件:notes.md(每天的概念口述稿)和 mistakes.md(题目 + 我写错的地方 + 正确思路一行)。第 8 周只回看这两个。

补充题源

主战场是自己的库。额外刷题:LeetCode 数据库题(简单+中等全刷)、pgexercises.com、牛客 SQL 篇(对齐国内面试口味)。

课程表

八周,公司的八个发展阶段

一条业务主线从零开始:每周是公司的一个阶段,表随业务逐张上线,知识点被当天的需求逼出来。点进去就是那周的 7 天日程。

W01 0/7

入职第一周:老板的第一张表

公司刚成立半年,只有十来个人,你是第一个和数据沾边的人。老板把后台导出的订单表丢给你:「咱全部的生意都在里面。」你的任务:把它变成一个能查的库,然后开始接老板一个接一个的问题。

目标:零基础起步,周五闭卷完成 30 道单表查询题;任何单表需求不查文档能写对。

进入本周 →
W02 0/7

第二批数据来了:多表世界

运营把后台的用户、商品、订单明细三张表导给你:「老板要按城市和品类看销售。」单张 orders 表答不了这些--你第一次要把多张表连起来查。月底还要交公司的第一份正式月报。

目标:多表报表查询不再靠试;能当场解释 LEFT JOIN 的过滤陷阱。

进入本周 →
W03 0/7

需求变绕了:子查询、CTE 与章法

商品越上越多,公司上线了类目体系(categories 表进场);运营的需求也从「统计一下」变成了「找出买过 A 又没买过 B 的用户」这种绕来绕去的活。你开始需要把复杂需求拆开、有章法地写的本事。

目标:拿到一个绕的需求,能拆成几步、有章法地写出来,而不是硬凑。

进入本周 →
W04 0/7

增长团队入场:窗口函数

公司拿到融资,增长团队进场,user_logins 登录日志接了进来。他们不问「总共多少」,问「排名第几」「比上个月涨了多少」「连续登录了多少天」。GROUP BY 不再够用--窗口函数进场,这也是面试的分水岭。

目标:这周决定你是「会 SQL」还是「SQL 不错」。经典题型形成肌肉记忆。

进入本周 →
W05 0/7

公司立规矩:建模、约束与事务

技术债集中爆发:脏数据、重复注册、改价没记录、并发下单差点把库存扣成负数。公司招了后端,你们决定把库重新立规矩,支付模块(payments 表)也在这周上线--你从「查数据的人」变成「能设计表的人」。

目标:从「查数据的人」变成「能设计表的人」。事务与隔离级别是八股必考区。

进入本周 →
W06 0/7

两年之后:索引与执行计划

时间快进:公司跑了一年多,订单冲到一百万,日报从一开始的秒出变成一分多钟。老板的原话:「它是不是坏了?」你的任务:让它快回去。

目标:看得懂 EXPLAIN ANALYZE,能说清一条查询为什么慢、加什么索引能救。

进入本周 →
W07 0/7

大促备战:优化实战与工程化

大促进入倒计时。技术负责人把「慢查询清单」「后台深分页超时」「历史数据归档」「秒杀防超卖」四座大山压过来--正是从初级到中级的分界线。

目标:从「能写对」到「能写好」,具备工程判断力--这是中级岗和初级岗的分界。

进入本周 →
W08 0/7

把故事讲出去:面试冲刺

八周(剧里是两年)的历练结束。你决定去更大的平台看看--手里这个从 0 长到百万订单、扛过大促的库,就是你最硬的项目经历。这一周不学新东西:把会的讲清楚,把手速练回来。

目标:不学新东西,只做两件事--把会的说清楚,把手速练回来。

进入本周 →
保持不忘

复习制度

每天 15 分钟复盘里,前 8 分钟固定用来做间隔重复。不额外占时间,但决定了第 8 周你还剩下多少。

时间点做什么用时
D + 1 默写昨天「练」栏的第 1 题,不看笔记 3 分钟
D + 3 重做 mistakes.md 里三天前的错题 5 分钟
D + 7 用一句话向「不懂 SQL 的人」解释一周前的概念 3 分钟
每周日 限时测评 + 错题归因(没懂 / 没记住 / 看错题) 已含在当日 2 小时内
每四周 综合测评,正确率低于 70% 则回炉重做该阶段 90 分钟
自测

高频考点自测清单

不用现在会。第 8 周时逐条自问,答不上来就回到对应的那天。能用一分钟讲清楚才算过。

查询与语义

01 SQL 的逻辑执行顺序是什么? → D06
02 count(*)、count(col)、count(distinct col) 有什么区别? → D04
03 LEFT JOIN 的过滤条件写在 ON 和 WHERE 里有何不同? → D10
04 NOT IN 遇到 NULL 会发生什么?为什么用 NOT EXISTS? → D17
05 UNION 和 UNION ALL 哪个快,为什么? → D12
06 ROW_NUMBER、RANK、DENSE_RANK 并列时分别怎么排? → D23
07 怎么查「连续登录 7 天」的用户? → D27
08 每个分类销量前 3 的商品,三种写法各是什么? → D20 / D23

性能与索引

09 索引为什么快?B+ 树相比其他结构好在哪? → D36
10 复合索引的最左前缀是什么意思?列顺序怎么定? → D38
11 哪些写法会让索引失效?各举一例。 → D41
12 EXPLAIN 里 rows 估算和 actual rows 差很多说明什么? → D39
13 Nested Loop、Hash Join、Merge Join 各自何时更优? → D40
14 OFFSET 深分页为什么慢?怎么改? → D44
15 一张亿级表要加索引,怎么做才不影响线上? → D45
16 你优化过最慢的一条 SQL 是什么?说说过程。 → D42 / D49

事务与设计

17 ACID 四个特性分别由什么机制保证? → D33
18 脏读、不可重复读、幻读的区别?各由哪个隔离级别解决? → D34
19 MVCC 是怎么实现「读不阻塞写」的? → D34
20 死锁是怎么产生的?怎么排查和避免? → D35
21 三大范式是什么?什么时候你会故意反范式? → D31
22 生产环境该不该用外键?说说你的取舍。 → D30
23 扣库存怎么防超卖?乐观锁和悲观锁怎么选? → D48
24 设计一个点赞功能的表,说说索引和扩展性。 → D52
交付

八周后你手上应该有这些

计划的价值不在于「学完了」,而在于留下能拿出来给面试官看的东西。

  • 一个跟业务一起长出来的电商库。7 张表按业务节奏逐张上线、百万级数据、完整 DDL 与索引设计,能当场讲清每个字段和每个索引存在的理由--这是「你的项目经历」,不是练习题。
  • 一份错题本 mistakes.md。约 60–100 条,按主题归类。面试前一晚只看这个。
  • 一份慢查询优化案例文档。W6「报表变慢」那周的真实记录:执行计划前后对比和耗时数据,直接对应简历上的一条经历。
  • 三份八股口述稿。索引 / 事务 / 设计题各 20 问,全部录过音、改过表达。
  • 一张临场易错清单。一页纸,面试前 10 分钟专用。
  • 一张知识地图。闭卷手绘,用来判断哪些分支还虚,作为后续 30 天维护的输入。