美国真正能练完这20页SQL题就是天花板了
Master These SQL Problems to Reach the Ceiling
第一部分:为什么说这是“天花板”
在北美求职,特别是Data Science和Data Analyst岗位,SQL是绕不过的坎。市面上的SQL题库五花八门,但大多是基础知识的堆砌,无法真正模拟大厂面试的深度和广度。而这份我们历时半年,结合了近百个一线大厂(Meta, Google, Amazon, Netflix等)的真实面试案例,总结出的20页SQL题,可以说是目前市面上最接近天花板的练习材料。
说它是“天花板”,主要有三个原因:
- 全面覆盖考点: 从基础的
GROUP BY、JOIN,到面试必考的窗口函数(Window Functions),再到考察思维深度的[Case](/blog/case-study-9206) WHEN和CTE(Common Table Expressions),以及涉及性能优化的复杂查询,这20页内容覆盖了99%的面试考点。 - 难度递进设计: 题目不是随机排列的,而是按照从易到难、从基础到综合的逻辑进行设计。让你在练习的过程中,能够逐步建立信心,平滑地从“能写出结果”过渡到“能写出最优解”。
- 贴近真实业务场景: 所有题目都基于真实的业务场景,比如用户行为分析、广告效果评估、电商GMV计算等。这不仅是考察你的SQL语法,更是考察你如何将业务问题转化为数据问题的能力。
第二部分:题目分类和难度分析
这20页的题目,我们可以大致分为五个核心模块:
-
基础查询与聚合 (Pages 1-4): 这是入门级别,主要考察对
SELECT,WHERE,GROUP BY,HAVING,ORDER BY的熟练运用。难度不大,但关键在于细节,比如WHERE和HAVING的区别,COUNT(DISTINCT ...)的使用等。 -
多表连接与子查询 (Pages 5-8): 考察
INNER JOIN,LEFT JOIN,RIGHT JOIN,FULL OUTER JOIN的使用。难点在于理解不同JOIN的逻辑,以及在复杂场景下如何通过子查询或CTE来拆解问题。 -
窗口函数 (Pages 9-14): 这是大厂面试的重中之重,也是区分候选人水平的关键。
ROW_NUMBER(),RANK(),DENSE_RANK(),LEAD(),LAG(),SUM(...) OVER (...)等都是必考点。这个模块的题目会让你彻底搞懂窗口函数的应用场景,比如计算Top N、移动平均、用户留存等。 -
复杂逻辑与场景应用 (Pages 15-18): 这个模块的题目通常没有标准答案,更考察你的逻辑思维能力。你需要熟练运用
CASE WHEN来进行条件判断,用CTE来构建清晰的查询逻辑,解决一些复杂的业务问题,比如“连续登录N天的用户”、“购物篮分析”等。 -
查询优化与性能 (Pages 19-20): 在你能写出正确答案之后,面试官会进一步追问:“How can you optimize this query?” 这个模块会教你如何思考查询性能,比如索引(Indexing)的作用、如何避免全表扫描、
EXISTSvsINvsJOIN的选择等。虽然不一定要求你现场写出最优的执行计划(Execution Plan),但你必须具备这方面的知识储备。
第三部分:精选题目详细解答
我们从每个模块中精选一道代表性的题目进行解析。
Question 1: (基础查询) Active User Calculation
The Task:
Given a logins table with user_id and login_date, write a query to find the number of monthly active users (MAU) for the last 3 months.
题目解析:
这道题看似简单,但考察了对日期函数和COUNT(DISTINCT ...)的理解。
最优解:
SELECT
DATE_TRUNC('month', login_date) AS login_month,
COUNT(DISTINCT user_id) AS mau
FROM
logins
WHERE
login_date >= DATE_TRUNC('month', CURRENT_DATE) - INTERVAL '2 months'
GROUP BY
1
ORDER BY
1;
解法对比:
常见错误: 有些同学可能会忘记使用DISTINCT,导致计算出的不是用户数,而是登录次数。或者在WHERE子句中对日期的处理不够精确。
次优解: 使用EXTRACT(YEAR FROM login_date)和EXTRACT(MONTH FROM login_date)来进行分组,虽然也能得到结果,但使用DATE_TRUNC更简洁,也更符合SQL的编程习惯。
Question 2: (窗口函数) Second Highest Salary
The Task:
Given an employees table with employee_id, department_id, and salary, write a query to find the second highest salary in each department. If a department has less than two employees, it should not be included in the result.
题目解析: 这是一道经典的Top N问题,是考察窗口函数的必考题。
最优解 (使用DENSE_RANK):
WITH RankedSalaries AS (
SELECT
department_id,
salary,
DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) as rnk
FROM
employees
)
SELECT
department_id,
salary
FROM
RankedSalaries
WHERE
rnk = 2;
解法对比:
为什么用DENSE_RANK而不是RANK或ROW_NUMBER? 如果存在并列薪水,RANK会在并列后跳过下一个排名(比如 1, 1, 3),ROW_NUMBER会给并列的记录分配不同的排名,只有DENSE_RANK(1, 1, 2)能保证在有并列的情况下,依然能准确找到“第二高”的薪水值。
次优解 (使用子查询): 也可以通过自连接或者复杂的子查询来实现,但代码会非常冗长且难以理解,性能也远不如窗口函数。
Question 3: (复杂逻辑) Consecutive Days Login
The Task:
Given a logins table with user_id and login_date, write a query to find all users who have logged in for at least 3 consecutive days.
题目解析:
“连续N天”是面试中的一个高频场景,考察你如何巧妙地处理日期序列。直接用JOIN会非常复杂,这里展示一个利用窗口函数LAG和DATE_DIFF的巧妙解法。
最优解:
WITH DateDiffs AS (
SELECT
user_id,
login_date,
login_date - LAG(login_date, 1, login_date) OVER (PARTITION BY user_id ORDER BY login_date) AS date_diff
FROM
logins
),
ConsecutiveGroups AS (
SELECT
user_id,
login_date,
SUM(CASE WHEN date_diff > 1 THEN 1 ELSE 0 END) OVER (PARTITION BY user_id ORDER BY login_date) AS grp
FROM
DateDiffs
),
GroupCounts AS (
SELECT
user_id,
grp,
COUNT() AS consecutive_days
FROM
ConsecutiveGroups
GROUP BY
user_id, grp
)
SELECT DISTINCT
user_id
FROM
GroupCounts
WHERE
consecutive_days >= 3;
解法思路: 这个解法的核心思想是“差分分组”。
1. 首先用LAG计算每次登录与上次登录的日期差。
- 如果日期差大于1天,说明连续登录中断了,我们用
SUM(...) OVER (...)来创建一个新的分组标记grp。
3. 这样,所有连续登录的记录都会被分到同一个grp里。
4. 最后,我们只需要计算每个grp里的记录数,就能知道连续登录的天数。
第四部分:如何系统性地练习
- 不要贪多求快: 每天花1-2小时,集中精力吃透2-3道题。关键不是刷题数量,而是理解每道题背后的逻辑和多种解法。
- 先思考,再看答案: 拿到一道题,先自己尝试写出一种解法,哪怕是暴力解法。然后再去看最优解,对比思考差异在哪里,为什么最优解更好。
- 举一反三: 在做完一道题后,想一想这道题的变种。比如“第二高薪水”可以变成“每个部门薪水前三的员工”。“连续3天登录”可以变成“找到最长连续登录天数”。这种主动思考能让你真正掌握一个知识点。
- 定期复习: 每周或每两周,回顾一下之前做过的题目,特别是那些你第一次没有做出来的难题,确保你已经完全掌握。
