表: 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