数据说话
我维护了一个SQL面试题库 dwsql.com,收录了字节跳动、腾讯、阿里巴巴、美团等23家公司的165道真实SQL面试真题。
做考点统计的时候,有一个数据让我印象很深:
窗口函数:45+ 次 ████████████████████████████
连续问题:20+ 次 █████████████
行列转换:15+ 次 █████████
留存分析:10+ 次 ██████
窗口函数以压倒性优势排在第一。这玩意躲不掉,只能正面刚。
今天用5道真题,把窗口函数的三板斧讲清楚:排名、偏移、聚合。
三板斧之一:ROW_NUMBER / RANK / DENSE_RANK
这三个函数长得像,面试官就爱问你它们有什么区别。
原始数据:A:100分 B:90分 C:90分 D:80分
ROW_NUMBER: A=1 B=2 C=3 D=4 (死编号,绝不复用)
RANK: A=1 B=2 C=2 D=4 (并列跳过)
DENSE_RANK: A=1 B=2 C=2 D=3 (并列不跳过)
选型口诀:常规 Top N 用 ROW_NUMBER;需要考虑并列时用 RANK;要求连续不跳号用 DENSE_RANK。
真题上场:
「各部门薪资 Top 3」 — 阿里爱考题
SELECT dept_id, emp_name, salary
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn
FROM employee
) t
WHERE rn <= 3;
这里为什么用 ROW_NUMBER 而不是 RANK?因为题目说"前3名",隐含了只要3个人的意思。如果题目改成"找出工资排名前三的员工(含并列)",那就得用 DENSE_RANK。
这种"用哪个排名函数"的判断,面试官会追问。理解三个函数的区别比会写语法更重要。
三板斧之二:LAG / LEAD
LAG 取前面行的值,LEAD 取后面行的值。语法完全对称:
LAG(列名, 偏移量, 默认值) OVER (PARTITION BY ... ORDER BY ...)
LEAD(列名, 偏移量, 默认值) OVER (PARTITION BY ... ORDER BY ...)
这两个函数是所有"连续问题"的基础砖块。连续登录、连续增长、留存率计算,本质上都是"当前行和前一行比较"。
真题上场:
「每年成绩都有所提升的学生」 — 美团真题
-- step1: 算每年总成绩
SELECT year, student, SUM(score) AS total
FROM scores GROUP BY year, student;
-- step2: 用 LAG 取去年的成绩
-- step3: 比大小
-- step4: 确定"每年"都在增长
核心就是一句:
LAG(total_score) OVER (PARTITION BY student ORDER BY year)
这一行代码拿到的"去年成绩",直接用 IF 比较就知道有没有进步了。
完整代码+中间结果见:dwql.com 真题解析
三板斧之三:聚合窗口函数 SUM/AVG/COUNT OVER
常规聚合(GROUP BY)把多行压成一行。窗口聚合保留每一行,在旁边附加一个计算结果。这个差异在面试中经常被拿来问。
真题上场:
「累计销售额」 — 考察基础但极高频
SELECT date, amount,
SUM(amount) OVER (ORDER BY date) AS cumulative
FROM sales;
不需要子查询,不需要 JOIN,一行搞定累计值。理解了这行 SQL,才算真正理解了 OVER 子句。
更复杂一点加个 PARTITION BY:
SELECT date, product, amount,
SUM(amount) OVER (PARTITION BY product ORDER BY date) AS cumulative
FROM sales;
按产品分组,各自算累计。这种写法在报表开发中非常高频。
连续问题的通解
面试中最难的题,基本都绕不开"连续"两个字。连续登录、连续增长、连续活跃……
通用解法分三步:
Step 1: 用 ROW_NUMBER 给每组内的行编号
Step 2: 用日期减去 ROW_NUMBER(得到"差值"grp)
Step 3: 连续的行 → grp相同 → 按grp分组统计
用伪代码表示这个核心逻辑:
-- 连续登录的天数
SELECT user_id, grp, COUNT(*) AS days
FROM (
SELECT *,
DATE_SUB(login_date,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date)) AS grp
FROM user_login
) t
GROUP BY user_id, grp;
这段代码是很多"连续问题"的标准模板。面试前把它写3遍,背下来都不亏。
连续问题的变体非常多,完整解法可以参考:dwsql.com 连续问题专题
刷题建议
时间有限的话,按这个优先级刷:
- 窗口函数基础 (1天) — ROW_NUMBER / RANK / LAG / SUM OVER,每个写3遍
- Top N + 排名 (0.5天) — 分组取前几名,理解三个排名函数的差异
- 连续问题 (1天) — 差值法的标准模板,搞懂一道就能解十道
- 留存率 + 环比 (0.5天) — LAG/LEAD 的实际应用
3天集中突破,窗口函数这一关就过了。剩下的就是在更多题上练习手感。
🚀 dwsql.com 窗口函数专题 — 14个窗口函数的完整教程,从 ROW_NUMBER 到 CUME_DIST,每个都有详细的示例和避坑指南。
对SQL面试有疑问?欢迎在评论区交流。更多真题解析可以在 dwsql.com 找到。