表: Employee
| Column Name | Type |
|---|---|
| id | int |
| name | varchar |
| salary | int |
| departmentId | int |
id 是该表的主键列(具有唯一值的列)。
departmentId 是 Department 表中 ID 的外键(reference 列)。
该表的每一行都表示员工的ID、姓名和工资。它还包含了他们部门的ID。
表: Department
| Column Name | Type |
|---|---|
| id | int |
| name | varchar |
id 是该表的主键列(具有唯一值的列)。
该表的每一行表示部门ID和部门名。
公司的主管们感兴趣的是公司每个部门中谁赚的钱最多。
一个部门的 高收入者 是指一个员工的工资在该部门的 不同 工资中 排名前三 。
编写解决方案,找出每个部门中 收入高的员工 。
以 任意顺序 返回结果表。
返回结果格式如下所示。
示例 1:
输入:
| Employee 表: | |||
|---|---|---|---|
| id | name | salary | departmentId |
| 1 | Joe | 85000 | 1 |
| 2 | Henry | 80000 | 2 |
| 3 | Sam | 60000 | 2 |
| 4 | Max | 90000 | 1 |
| 5 | Janet | 69000 | 1 |
| 6 | Randy | 85000 | 1 |
| 7 | Will | 70000 | 1 |
| Department 表: | |
|---|---|
| id | name |
| 1 | IT |
| 2 | Sales |
| 输出: | ||
|---|---|---|
| Department | Employee | Salary |
| IT | Max | 90000 |
| IT | Joe | 85000 |
| IT | Randy | 85000 |
| IT | Will | 70000 |
| Sales | Henry | 80000 |
| Sales | Sam | 60000 |
解释:
在IT部门:
- Max的工资最高
- 兰迪和乔都赚取第二高的独特的薪水
- 威尔的薪水是第三高的
在销售部:
- 亨利的工资最高
- 山姆的薪水第二高
- 没有第三高的工资,因为只有两名员工
题解:
啊啊啊啊,花了俩小时才搞出来。。。。
不知道还有没有更简单的。。。我这应该也算暴力的一种吧。
1.给每个部门的人员薪资做个薪资等级排名,并且部门之间薪资相同的薪资等级相同,标记为salaryRank。
找每个部门前三的问题就变成了找salaryRank<=3的员工。至于部门名字,直接把Department表当成码表即可。
之后找前n等级的只需要更改1个数字就行,即salaryRank<=n
2.编写每个部门的人员薪资做个薪资等级排名临时表
包括Employee.departmentId,Employee.name,Employee.Salary
还有最重要的需要处理的Employee.salaryRank。
在这里使用窗口函数和临时变量创建临时表。
3.首先我们先利用窗口函数的row_number()来个普通版本的各部门的等级排名,即薪资重复的情况不考虑。
select departmentId,salary,name
,row_number() over(partition by departmentId order by salary desc) salaryRank
from employee
可以发现下图的查询结果,各部门的薪资都有个薪资等级salaryRank,但目前薪资重复时,薪资等级并不是相同的,因此下面我们通过临时变量处理下薪资相同时的情况。
4.需要考虑的情况是,各部门按照薪资排序后,如果当前的salary与上一个salary相同时,薪资等级需要与上一个的薪资等级相同,因此减去当前重复的个数即可。
(即Randy的薪资是第1发生重复的,即实际的薪资等级=row_numbe() - 1,同理第n个重复的就是row_numbe() - n)
加上临时变量@salary(临时的salary)和@ranknum(重复的个数),并且要注意部门发生变化时,@ranknum记得初始化。
select departmentId,salary,name
, row_number() over(partition by departmentId order by salary desc)
- (case when @departmentId = departmentId then (case when @salary = salary then @ranknum:=@ranknum+1 else @ranknum:=@ranknum end) else @ranknum:=0 end) salaryRank
,@salary := salary temp1
,@departmentId := departmentId temp2
from employee,(select @ranknum:=0,@salary:=null,@departmentId:=null) temp
查询如下图所示。
注意如果@ranknum没有初始化,只写了像下面一样的。
select departmentId,salary,name
, row_number() over(partition by departmentId order by salary desc)
- (case when @salary = salary then @ranknum:=@ranknum+1 else @ranknum:=@ranknum end) salaryRank
,@salary := salary temp1
from employee,(select @ranknum:=0,@salary:=null) temp
我们会发现Henry的薪资等级salaryRank变成了0,这是因为上个分组的@ranknum没有重新初始化为0,上个分组有1个重复薪资,导致之后的分组salaryRank全都偏移了1位。
并且如果@ranknum没有初始化,每个分组的重复数量都会累加到@ranknum中,甚至会导致薪资等级salaryRank变成负数。
因此case when @department_id = department_id then () else @ranknum:=0 end 这一句就是为了初始化而加上的。

5.现在我们有了1张临时表,后面的就简单了吧!
临时表
直接选择salaryRank<=3的即可得到结果。
select d.name Department, t.name Employee,Salary
from 临时表
left join department d
on d.id = t.departmentId
where salaryRank <=3
order by t.departmentId,t.Salary desc
最终我们可以得到代码:
with t as (
select departmentId,salary,name
, row_number() over(partition by departmentId order by salary desc)
- (case when @departmentId = departmentId then (case when @salary = salary then @ranknum:=@ranknum+1 else @ranknum:=@ranknum end) else @ranknum:=0 end) salaryRank
,@salary := salary temp1
,@departmentId := departmentId temp2
from employee,(select @ranknum:=0,@salary:=null,@departmentId:=null) temp
)
select d.name Department, t.name Employee,Salary
from t
left join department d
on d.id = t.departmentId
where salaryRank <=3
order by t.departmentId,t.Salary desc
最终得到结果:
1 条评论
陈刚陈:文章真不错https://haodnf.cn/gonglue/xinshouzhinan/20240705/18.html