本章目标:知道去哪里刷 SQL、按什么顺序刷、每道题练什么,能独立完成 60 道在线题(LeetCode 50 + 牛客 10)。 前置知识:SQL 基础到窗口函数已经学过,并且跑通过前面章节的玩具表示例。 预计用时:90 分钟读完并开始刷题,刷完 60 题通常需要 3–4 周。
题目前的方框是「已刷」标记:做完一道就勾上。同一道题如果在别的章节也出现,共用同一个标记,勾一次即可;全站进度只算一次。
1. 题库入口
| 平台 | 入口 | 适合 | 说明 |
|---|---|---|---|
| LeetCode 数据库题库 | leetcode.cn/problem-list/database | 系统刷题、按难度筛 | 中文站,部分题需要会员 |
| LeetCode 高频 SQL 50 题 | leetcode.cn/studyplan/sql-free-50 | 零基础到面试 | 官方学习计划,可作为本清单之外的备选 |
| 牛客 SQL 快速入门 | nowcoder.com/exam/oj?tab=SQL篇&topicId=199 | 中文题干、入门 | 免费,题解和讨论多 |
| 牛客 SQL 基础进阶 | topicId=341 | 聚合、多表 | 难度比快速入门高一档 |
| 牛客 SQL 进阶挑战 | topicId=240 | 窗口、复杂查询 | 适合刷完 LeetCode 简单题后做 |
| 牛客 MySQL 必知必会 | topicId=298 | 语法查漏 | 覆盖基础语法和函数 |
| 牛客大厂面试实战 | topicId=268 | 面试前冲刺 | 更接近真实面试题 |
注意:本章的 50 道 LeetCode 是自选清单,不是官方「高频 SQL 50」。官方计划适合当第二遍复习,不要把它和本清单混着数题。
建议:以 LeetCode 为主,牛客做补充。LeetCode 的判题和题解质量更稳定;牛客中文题干更贴近国内面试。两边不要重复刷同一类题。
2. 刷题顺序
不要从“困难”开始,也不要按题号顺序刷。按能力递进:
基础查询与过滤
↓
聚合与分组
↓
多表 JOIN
↓
排名与窗口函数
↓
日期、留存与连续问题
↓
综合与困难题
每一组刷完,回到玩具库 project/sql_demo.duckdb,用 demo_* 小表把同类逻辑再写一遍。在线题练“题型”,玩具表练“口径拆解”,两者不能互相替代。
4 周节奏
| 周 | 内容 | 题量 | 完成标准 |
|---|---|---|---|
| 第 1 周 | 3.1 基础 + 3.2 聚合 | 17 | 简单题 10 分钟内写完,不查语法 |
| 第 2 周 | 3.3 JOIN + 3.4 排名窗口前半 | 13 | 能说清 JOIN 后行数为什么变多 |
| 第 3 周 | 3.4 窗口后半 + 3.5 日期留存 | 14 | 能手写 ROW_NUMBER、LAG、连续区间模板 |
| 第 4 周 | 3.6 综合 + 错题重做 + 牛客 10 题 | 6 + 10 | 困难题先写口径,再看题解 |
时间不够时优先完成标记为「简单」和「中等」的题;困难题至少看懂思路。
3. 题目清单
下面按主题列出 50 道 LeetCode 题,点击标题进入题目页;标 🔒 的需要会员,旁边给出免费替代题。牛客的 10 道题在第 3.7 节,从题单中自选。
3.1 基础查询与过滤
练 SELECT、WHERE、NULL、DISTINCT、简单子查询。
| 题号 | 题目 | 难度 | 考点 | 免费替代 |
|---|---|---|---|---|
| 595 | 大的国家 | 简单 | 多条件 OR、括号 | — |
| 175 | 组合两个表 | 简单 | LEFT JOIN | — |
| 183 | 从不订购的客户 | 简单 | NOT IN / LEFT JOIN 空值 | — |
| 620 | 有趣的电影 | 简单 | 过滤、排序 | — |
| 1148 | 文章浏览 I | 简单 | DISTINCT、排序 | — |
| 584 | 寻找用户推荐人 | 简单 | NULL 判断 | — |
| 619 | 只出现一次的最大数字 | 简单 | 聚合后空结果 | — |
完成标准:能解释 = NULL 为什么不对;能用 NOT IN 和 LEFT JOIN ... IS NULL 两种写法找出缺失记录。
对应牛客:SQL 快速入门。
3.2 聚合与分组
练 COUNT、SUM、AVG、多列分组、分母口径。
| 题号 | 题目 | 难度 | 考点 | 免费替代 |
|---|---|---|---|---|
| 586 | 订单最多的客户 | 简单 | 分组计数、排序取第一 | — |
| 596 | 超过 5 名学生的课 | 简单 | GROUP BY + HAVING | — |
| 1050 | 合作过至少三次的演员和导演 | 简单 | GROUP BY + HAVING | — |
| 1075 | 项目员工 I | 简单 | 平均值的四舍五入 | — |
| 1082 | 销售分析 I 🔒 | 简单 | 分组求和 | 1068 产品销售分析 I |
| 1083 | 销售分析 II 🔒 | 简单 | 过滤后再聚合 | 1084 销售分析 III |
| 1084 | 销售分析 III | 简单 | 分组 + 条件全部满足 | — |
| 1251 | 平均售价 | 简单 | 加权平均、NULL 处理 | — |
| 1280 | 学生们参加各科测试的次数 | 简单 | 交叉连接、补零 | — |
| 1327 | 列出指定时间段内所有的下单产品 | 简单 | 日期过滤 + HAVING | — |
完成标准:写聚合前先说清“分母是谁”;能处理分组后没有数据的补零问题。
对应牛客:SQL 基础进阶。
3.3 多表 JOIN 与集合
练 INNER/LEFT JOIN、自连接、UNION、关联膨胀。
| 题号 | 题目 | 难度 | 考点 | 免费替代 |
|---|---|---|---|---|
| 181 | 超过经理收入的员工 | 简单 | 自连接 | — |
| 182 | 查找重复的电子邮箱 | 简单 | GROUP BY + HAVING | — |
| 196 | 删除重复的电子邮箱 | 简单 | DELETE + 自连接(DML 选做) | — |
| 197 | 上升的温度 | 简单 | 自连接 + 日期差 | — |
| 1068 | 产品销售分析 I | 简单 | 三表 JOIN | — |
| 1378 | 替换员工 ID 的唯一标识符 | 简单 | LEFT JOIN 选列 | — |
| 1070 | 产品销售分析 III | 中等 | 分组取最早年份 | — |
| 1158 | 市场分析 I | 中等 | LEFT JOIN + 补零 | — |
完成标准:能预测 JOIN 前后行数变化;知道一对多 JOIN 后直接 SUM 会重复计算。
对应牛客:MySQL 必知必会。
3.4 排名与窗口函数
练 ROW_NUMBER、RANK、DENSE_RANK、SUM OVER、LAG/LEAD。
| 题号 | 题目 | 难度 | 考点 | 免费替代 |
|---|---|---|---|---|
| 176 | 第二高的薪水 | 中等 | 子查询 / LIMIT OFFSET | — |
| 177 | 第 N 高的薪水 | 中等 | 自定义函数、NULL | — |
| 178 | 分数排名 | 中等 | DENSE_RANK | — |
| 184 | 部门工资最高的员工 | 中等 | 分组 Top1 + JOIN | — |
| 1112 | 每位学生的最高成绩 🔒 | 中等 | 分组取 Top1、并列处理 | 184 部门工资最高的员工 |
| 1204 | 最后一个能进入巴士的人 | 中等 | 累计求和 | — |
| 1321 | 餐馆营业额变化增长 | 中等 | 移动窗口 | — |
| 1341 | 电影评分 | 中等 | 多个 Top1 + UNION | — |
| 180 | 连续出现的数字 | 中等 | 自连接 / 窗口 | — |
| 185 | 部门工资前三高的所有员工 | 困难 | 分组 TopN | — |
| 601 | 体育馆的人流量 | 困难 | 连续区间 | — |
完成标准:能手写“分组 TopN”和“连续 N 天”两个模板;能说清 ROW_NUMBER 与 RANK 在并列时的差异。
对应牛客:SQL 进阶挑战。
3.5 日期、留存与连续问题
练日期函数、留存、连续登录、gap-and-islands。
| 题号 | 题目 | 难度 | 考点 | 免费替代 |
|---|---|---|---|---|
| 1141 | 查询近 30 天活跃用户数 | 简单 | 日期过滤 | — |
| 1517 | 查找拥有有效邮箱的用户 | 简单 | 正则 | — |
| 1527 | 患某种疾病的患者 | 简单 | 字符串匹配 | — |
| 1193 | 每月交易 I | 中等 | 按月分组、条件聚合 | — |
| 1205 | 每月交易 II 🔒 | 中等 | 多指标合并 | 1193 每月交易 I |
| 1107 | 每日新用户统计 🔒 | 中等 | 首次登录 | 550 游戏玩法分析 IV |
| 1454 | 活跃用户 🔒 | 中等 | 连续登录 | 180 连续出现的数字 |
| 1225 | 报告系统状态的连续日期 🔒 | 困难 | 连续区间 | 601 体育馆的人流量 |
完成标准:能区分“首次出现”和“当天活跃”;能用首购 cohort 说清留存的分母。
对应牛客:大厂面试实战。
3.6 综合与困难题
这些题一道顶三道,放在最后做。做不出来先看题解思路,隔一天再重写。
| 题号 | 题目 | 难度 | 考点 | 免费替代 |
|---|---|---|---|---|
| 262 | 行程和用户 | 困难 | 多条件过滤 + 比率 | — |
| 579 | 查询员工的累计薪水 🔒 | 困难 | 窗口 + 排除最近一月 | 1204 最后一个能进入巴士的人 |
| 569 | 员工薪水中位数 🔒 | 困难 | 中位数 | 可跳过(无等价免费题) |
| 615 | 平均工资:部门与公司比较 🔒 | 困难 | 多层聚合 | 184 部门工资最高的员工 |
| 618 | 学生地理信息报告 🔒 | 困难 | 行列转换 | 1179 重新格式化部门表 |
| 1097 | 游戏玩法分析 V 🔒 | 困难 | 留存 | 550 游戏玩法分析 IV |
3.7 牛客 10 题
下面 4 个题单各选 2–3 道,凑满 10 道。优先选题面带“近 7 天”“同比”“留存”“连续”的题:
| 题单 | 入口 | 选题重点 |
|---|---|---|
| SQL 快速入门 | topicId=199 | 基础过滤、聚合 |
| SQL 基础进阶 | topicId=341 | 多表、子查询 |
| SQL 进阶挑战 | topicId=240 | 窗口、TopN |
| 大厂面试实战 | topicId=268 | 业务指标、留存 |
4. 怎么刷才有效
4.1 先写口径,再写 SQL
每道题先用一句话说清:统计对象是谁、分母是什么、要不要去重、NULL 怎么算。说不清就先别写。面试里答错口径,比语法写错更致命。
4.2 限时,但不要一直限时
- 简单题:10 分钟内。
- 中等题:20–25 分钟。
- 困难题:35 分钟;超时先看题解思路,不直接抄代码。
限时是为了模拟面试,不是为了逼自己。学习阶段卡住 20 分钟就该看提示。
4.3 一题至少两种写法
同一道题试着用 JOIN、子查询、窗口函数各写一遍,比较可读性和执行计划。面试追问“还有别的写法吗”时,这就是加分项。
4.4 错题本只记三行
题目:185 部门工资前三高的所有员工
错因:DENSE_RANK 写成了 ROW_NUMBER,并列名次被跳过
下次:分组 TopN 先确认“并列算不算同一名次”
不要抄整段答案。记错因和下次的动作,复习时才用得上。
4.5 刷完必须能脱稿
关掉题解,在白纸或空编辑器里重写主体逻辑。能写出来才算过;只是“看懂了”不算。
4.6 勾选只是记录,不是完成标准
题目前面的方框用于记录“已经做过”,方便看进度。真正算过关的标准是:能先写口径,再独立写出可执行 SQL,并能解释边界情况。
5. 常见坑
| 坑 | 表现 | 处理 |
|---|---|---|
| 只刷简单题 | 数量很好看,面试中等题就卡 | 简单题控制在 10 道以内,尽快进入聚合和窗口 |
| 看题解等于学会 | 下次同类型还是不会 | 关掉题解重写,隔天再做一遍 |
| 不写口径 | SQL 能跑,但答案含义不对 | 每题先写一句口径 |
| 忽略 NULL | NOT IN 遇 NULL 返回空、平均值分母变少 |
先判断字段是否可能为空 |
| 忽略并列 | TopN 用 ROW_NUMBER 丢掉并列 |
看题目要求选 RANK 或 DENSE_RANK |
| 只在一个平台刷 | 题型覆盖不全 | LeetCode 为主,牛客补中文题干和面试题 |
| 刷完不复盘 | 错过的题继续错 | 错题本 + 隔天重做 |
6. 练习
本章的练习就是在线题本身,不再提供本地答案。
练习 1:完成本清单 50 题
按 3.1–3.6 的顺序刷,做完一道勾一道。每完成一组,回到玩具库用 demo_orders、demo_order_items 写一个同类查询。
练习 2:完成 10 道窗口函数题
从 3.4 节挑 10 道,要求全部用窗口函数写一遍,并至少有一道用自连接再写一遍。
练习 3:完成 5 道连续/留存题
从 3.5 节挑 5 道,重点练“连续日期”和“留存”的模板。做完后用 demo_orders 按首购月 cohort 复现一次回购率。
练习 4:整理自己的错题本
按 4.4 节的格式,记录至少 10 道错题。每道写清错因和下次动作。
7. 面试延伸
面试题:你平时怎么练 SQL?
是什么:我用 LeetCode 数据库题库和牛客 SQL 题单练题,按“基础查询 → 聚合 → JOIN → 窗口 → 日期与连续问题”的顺序推进,LeetCode 为主、牛客补中文面试题。
解决什么问题:在线判题能暴露边界情况和语法错误,题解和讨论能补一题多解;本地项目数据则用来练业务口径和对账,两者互补。
我的实操边界:我完成过 LeetCode 高频 SQL 50 和若干窗口函数题,能独立写出分组 TopN、连续登录、留存的主体逻辑;复杂困难题和付费题还没有全部刷完。
面试题:遇到没见过的 SQL 题怎么办?
是什么:先把问题翻译成“输入表 → 目标粒度 → 需要的聚合或窗口 → 过滤条件”,再落 SQL。
解决什么问题:面试题往往包装了业务场景,拆成这四步就不会一上来就写 JOIN。写完后用一个小例子手算预期结果,再检查边界:NULL、重复、并列、空分组。
我的实操边界:我在练习中按这个顺序拆题,能处理常见的 TopN、留存、连续登录;对特别复杂的多指标合并题,仍需要先在纸上画表关系。
8. 本章验收
- 能说出 LeetCode 数据库题库和牛客 SQL 题单的入口,知道各自适合什么。
- 力扣 50 题刷到 50/50(页面勾选),牛客完成 10 道。
- 能独立写出分组 TopN、连续登录、留存三类题的模板。
- 每道做过的题都能先说口径,再写 SQL。
- 有至少 10 道题的错题本,记录错因和下次动作。
- 能关掉题解,在白纸或空编辑器里重写任意一道中等题。
上一章:窗口函数。下一章:MySQL 与索引优化。
忘了玩具表字段就回到SQL 基础 2.2 节查表结构。