聊天讨论 面试官让我查各部门工资最高的员工,我用 AI 三秒写出窗口函数,他愣了

193577746(kyriewen) · 2026年05月22日 · 15 次阅读

上个月面一家大厂,技术面第二轮,面试官出了一个 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),销售部返回赵六。

二、AI 生成的标准答案

我在 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_RANKRANK 区别就大了。

三、窗口函数的执行顺序(讲清楚才能加分)

面试官接着问:“窗口函数是在 SQL 哪个阶段执行的?”

我回答:在 WHERE、GROUP BY 之后,在 ORDER BY 之前。具体顺序:

  1. FROM → JOIN
  2. WHERE → 过滤行
  3. GROUP BY → 分组
  4. 聚合函数计算
  5. HAVING → 过滤分组
  6. 窗口函数 → 在这里计算排名
  7. ORDER BY
  8. LIMIT

所以,窗口函数的结果可以在 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?

同样的逻辑: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 能帮你写出任何代码,但能讲清楚为什么、怎么改、有什么坑,才是你的真本事。

你面试遇到过哪些 “想不起来语法” 的瞬间?后来怎么过的?

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号