这道题是 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高的员工姓名、部门和薪资。
DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) 排名ROW_NUMBER vs RANK vs DENSE_RANK:
ROW_NUMBER:1,2,3,4... 即使值相同也不并列RANK:1,1,3,4... 值相同并列,后续跳过DENSE_RANK:1,1,2,3... 值相同并列,后续不跳过维度 | 评分 |
|---|---|
题目难度 | ⭐️⭐️ |
题目清晰度 | ⭐️⭐️⭐️⭐️⭐️ |
业务常见度 | ⭐️⭐️⭐️⭐️⭐️ |
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 的效果。
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。
NTILE(10) OVER (PARTITION BY dept_id ORDER BY salary DESC) → 取 NTILE=1 的组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人 |
子查询 + WHERE | Spark SQL 筛选排名,替代 QUALIFY |
CREATE TABLEIFNOTEXISTS t13_zj_employee (
idINT, nameSTRING, dept_id STRING
);
CREATETABLEIFNOTEXISTS t13_zj_salary (
idINT, salary BIGINT
);
INSERTINTO t13_zj_employee VALUES
(1,'张三','D001'),(2,'李四','D001'),(3,'王五','D001'),(4,'赵六','D001'),
(5,'孙七','D002'),(6,'周八','D002'),(7,'吴九','D002');
INSERTINTO t13_zj_salary VALUES
(1,50000),(2,45000),(3,45000),(4,30000),(5,60000),(6,55000),(7,40000);