跳到主要内容

字节跳动大数据面试SQL-每个部门工资前3高的员工

一、题目背景

这道题是 LeetCode 185 的原题变体,字节跳动、阿里、腾讯的面试中频繁出现。它考察窗口函数中最常用的 ROW_NUMBER / RANK / DENSE_RANK 三者之间的区别——这是面试官最喜欢追问的点。

业务场景:HR 系统需要出"各部门薪资 Top 3 报表",用于薪酬对标分析。注意:如果第3名有并列,用 DENSE_RANK 会包含所有并列者。

二、题目

有两张表:员工表 t13_zj_employee 和薪资表 t13_zj_salary。请找出每个部门工资前3高的员工。

t13_zj_employee 表

+----+--------+----------+
| id | name | dept_id |
+----+--------+----------+
| 1 | 张三 | D001 |
| 2 | 李四 | D001 |
| 3 | 王五 | D001 |
| 4 | 赵六 | D001 |
| 5 | 孙七 | D002 |
| 6 | 周八 | D002 |
| 7 | 吴九 | D002 |
+----+--------+----------+

t13_zj_salary 表

+----+--------+
| id | salary |
+----+--------+
| 1 | 50000 |
| 2 | 45000 |
| 3 | 45000 |
| 4 | 30000 |
| 5 | 60000 |
| 6 | 55000 |
| 7 | 40000 |
+----+--------+

期望输出:每个部门薪资前3高的员工姓名、部门和薪资。

三、思路分析

  1. JOIN 两表关联员工和薪资
  2. 用窗口函数 DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) 排名
  3. WHERE rn 小于等于 3 取前3

ROW_NUMBER vs RANK vs DENSE_RANK

  • ROW_NUMBER:1,2,3,4... 即使值相同也不并列
  • RANK:1,1,3,4... 值相同并列,后续跳过
  • DENSE_RANK:1,1,2,3... 值相同并列,后续不跳过
维度评分
题目难度⭐️⭐️
题目清晰度⭐️⭐️⭐️⭐️⭐️
业务常见度⭐️⭐️⭐️⭐️⭐️

四、逐步推导

步骤1:JOIN 并排名

SELECT
e.name,
e.dept_id,
s.salary,
DENSE_RANK() OVER (PARTITION BY e.dept_id ORDER BY s.salary DESC) AS rn
FROM t13_zj_employee e
JOIN t13_zj_salary s ON e.id = s.id;
+-------+----------+---------+-----+
| name | dept_id | salary | rn |
+-------+----------+---------+-----+
| 张三 | D001 | 50000 | 1 |
| 李四 | D001 | 45000 | 2 |
| 王五 | D001 | 45000 | 2 |
| 赵六 | D001 | 30000 | 3 |
| 孙七 | D002 | 60000 | 1 |
| 周八 | D002 | 55000 | 2 |
| 吴九 | D002 | 40000 | 3 |
+-------+----------+---------+-----+
7 rows selected (8.815 seconds)(https://www.dwsql.com)

D001:张三(1)、李四和王五并列(2)、赵六(3)。前3包含4人——这正是 DENSE_RANK 的效果。

步骤2:筛选前3

SELECT name, dept_id, salary
FROM (
SELECT
e.name, e.dept_id, s.salary,
DENSE_RANK() OVER (PARTITION BY e.dept_id ORDER BY s.salary DESC) AS rn
FROM t13_zj_employee e
JOIN t13_zj_salary s ON e.id = s.id
) t
WHERE rn <= 3;

最终结果

+-------+----------+---------+
| name | dept_id | salary |
+-------+----------+---------+
| 张三 | D001 | 50000 |
| 李四 | D001 | 45000 |
| 王五 | D001 | 45000 |
| 赵六 | D001 | 30000 |
| 孙七 | D002 | 60000 |
| 周八 | D002 | 55000 |
| 吴九 | D002 | 40000 |
+-------+----------+---------+
7 rows selected (0.655 seconds)(https://www.dwsql.com)

D001 有 4 人(因为李四和王五并列第二,赵六排第三也被包含)。如果面试官说"只要严格3个人",把 DENSE_RANK 换成 ROW_NUMBER 即可。

五、常见坑点

坑1:RANK vs DENSE_RANK vs ROW_NUMBER

面试官大概率会追问三者区别。简洁答案:ROW_NUMBER 不并列,RANK 并列跳号,DENSE_RANK 并列不跳号。

坑2:排序方向

窗口函数默认 ORDER BY 是升序。取"前N高"必须显式写 DESC。忘记写 DESC 会取到工资最低的N个人。

坑3:WHERE vs QUALIFY

Spark SQL 不支持 QUALIFY(某些数据库如 Snowflake 支持)。必须用子查询 + WHERE。

六、举一反三

  1. 每组前N%NTILE(10) OVER (PARTITION BY dept_id ORDER BY salary DESC) → 取 NTILE=1 的组
  2. 累计薪资占比SUM(salary) OVER (PARTITION BY dept_id ORDER BY salary DESC) / SUM(salary) OVER (PARTITION BY dept_id) 计算累计占比

七、知识点总结

考点说明
PARTITION BY按部门分区,排名在区内独立计算
ORDER BY DESC降序排列,最高薪排第1
DENSE_RANK并列不跳号,前3可能多于3人
子查询 + WHERESpark SQL 筛选排名,替代 QUALIFY

八、建表语句和数据插入

点击展开 DDL & DML
CREATE TABLE IF NOT EXISTS t13_zj_employee (
id INT, name STRING, dept_id STRING
);
CREATE TABLE IF NOT EXISTS t13_zj_salary (
id INT, salary BIGINT
);

INSERT INTO t13_zj_employee VALUES
(1,'张三','D001'),(2,'李四','D001'),(3,'王五','D001'),(4,'赵六','D001'),
(5,'孙七','D002'),(6,'周八','D002'),(7,'吴九','D002');

INSERT INTO t13_zj_salary VALUES
(1,50000),(2,45000),(3,45000),(4,30000),(5,60000),(6,55000),(7,40000);
📱关注公众号

「数据仓库技术」文章同步更新,不错过每一篇干货

微信公众号二维码
💬加群交流

备注「数据仓库技术」加入社群,每日一道大厂SQL真题

交流微信二维码

你可能还想看