上个月面一家大厂,技术面第二轮,面试官出了一个 SQL 题:“查每个部门工资最高的员工。” 我脑子里闪过一堆解法:子查询、自连接、group by……但都太绕,而且还容易漏掉并列第一。我打开 Cursor,输入:“用窗口函数写一个 SQL,查出每个部门工资最高的员工,如果最高工资并列全部返回。” 三秒后 SQL 生成。面试官看了一眼,说:“你是我见过第一个用窗口函数还讲得清原理的候选人。” 后来我拿到了 offer。
SQL 面试题里,“分组取 Top N” 是经典中的经典。尤其是 “每个部门工资最高的员工”,考频率仅次于两数之和。很多人能写出来,但写出来的往往是子查询嵌套自连接,性能差、代码丑、还容易漏掉并列的情况。
而窗口函数(ROW_NUMBER / RANK / DENSE_RANK)才是这道题的标准答案,但很多人一紧张就忘了语法:OVER 里面怎么写?PARTITION BY 和 ORDER BY 怎么配?RANK 和 ROW_NUMBER 有什么区别?
其实不用背,AI 一秒就给你写出来。但关键是:你要能讲清楚为什么选这个函数,以及窗口函数的执行顺序。今天我就用这个真实面试题,教你用 AI 秒解 SQL 难题,顺便讲透窗口函数。
金句:面试官考的不是你背不背得出语法,而是你懂不懂 “为什么” 和 “选哪个”。
表结构
CREATE TABLE employee (
id INT,
name VARCHAR(50),
department VARCHAR(50),
salary INT
);
需求:查询每个部门工资最高的员工。如果最高工资有并列(比如两人都是 10000),都要返回。
示例数据 | id | name | department | salary | | ---- |------| ------------ |--------| | 1 | 张三 | 技术部 | 12000 | | 2 | 李四 | 技术部 | 10000 | | 3 | 王五 | 技术部 | 12000 | | 4 | 赵六 | 销售部 | 9000 |
期望输出:技术部返回张三和王五(并列 12000),销售部返回赵六。
我在 Cursor 里输入:
用 MySQL 语法,写一个 SQL 查询:每个部门工资最高的员工。如果最高工资有多人,全部返回。要求使用窗口函数,并解释执行顺序。
AI 输出:
WITH ranked AS (
SELECT
*,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rnk
FROM employee
)
SELECT id, name, department, salary
FROM ranked
WHERE rnk = 1;
为什么用 RANK 而不是 ROW_NUMBER?
ROW_NUMBER 会给每人一个唯一序号,并列的也会强行分出先后,导致只能返回一人。RANK 会为相同工资分配相同排名,并列第一就都返回。面试官紧接着追问:那 DENSE_RANK 呢?答:DENSE_RANK 排名连续,但本题只需要最高工资,效果与 RANK 一样(因为只取 rnk=1)。但如果有第二高需求,DENSE_RANK 和 RANK 区别就大了。
面试官接着问:“窗口函数是在 SQL 哪个阶段执行的?”
我回答:在 WHERE、GROUP BY 之后,在 ORDER BY 之前。具体顺序:
所以,窗口函数的结果可以在 WHERE 里用吗?不能,因为 WHERE 在窗口函数之前执行。所以必须用 CTE 或子查询先计算排名,再在外部 WHERE 筛选。
AI 生成的代码正是这样做的:WITH ranked AS ( ... ) 先算排名,再 WHERE rnk = 1。面试官听到这里,点了头。
可以用子查询 + 关联,但性能差且代码臃肿:
SELECT e1.*
FROM employee e1
LEFT JOIN employee e2
ON e1.department = e2.department
AND e1.salary < e2.salary
WHERE e2.id IS NULL;
这个解法的逻辑是:找出不存在同部门更高工资的人。优点是跨数据库通用,缺点是不好理解,而且对于大表性能差(因为自连接扫描两次)。
用窗口函数,不仅快(一次扫描),而且清晰易维护。所以现代 SQL 强烈推荐。
金句:不用窗口函数的 SQL 优化,就像不用箭头函数的 JS——能跑,但不优雅。
同样的逻辑:AI 帮我生成语法,我负责解释原理。他知道我背不出完整的 RANK 语法(正常人谁背?),但我知道什么时候用 RANK、窗口函数执行顺序、如何改造成其他需求。这就够了。
面试官可能接着问:“如果我要每个部门第二高工资呢?”
很简单,把 WHERE rnk = 1 改成 WHERE rnk = 2 就行。但注意:如果第二名是并列(比如两人都是 9000),RANK 会跳过第三名的序号(比如排名:1,2,2,4),DENSE_RANK 则连续(1,2,2,3)。你需要问清楚需求。
你可以用这个数据自测:
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(20),
department VARCHAR(20),
salary INT
);
INSERT INTO employee VALUES
(1,'张三','技术部',12000),
(2,'李四','技术部',10000),
(3,'王五','技术部',12000),
(4,'赵六','销售部',9000);
然后运行上面的窗口函数 SQL,输出应该是:张三、王五、赵六。
面试题在变,但考察的核心没变:理解原理 > 背诵语法。AI 能帮你写出任何代码,但能讲清楚为什么、怎么改、有什么坑,才是你的真本事。
你面试遇到过哪些 “想不起来语法” 的瞬间?后来怎么过的?