Leetcode185.部门工资前三高的所有员工(困难)

题目
Employee 表包含所有员工信息,每个员工有其对应的工号 Id,姓名 Name,工资 Salary 和部门编号 DepartmentId 。

+----+-------+--------+--------------+
| 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    |
+----+----------+

编写一个 SQL 查询,找出每个部门获得前三高工资的所有员工。例如,根据上述给定的表,查询结果应返回:

+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| IT         | Randy    | 85000  |
| IT         | Joe      | 85000  |
| IT         | Will     | 70000  |
| Sales      | Henry    | 80000  |
| Sales      | Sam      | 60000  |
+------------+----------+--------+

解释:

IT 部门中,Max 获得了最高的工资,Randy 和 Joe 都拿到了第二高的工资,Will 的工资排第三。销售部门(Sales)只有两名员工,Henry 的工资最高,Sam 的工资排第二。

审题
思路 给Employee进行分组排序即可 然后选取排名小于等于三的即可 再与department表连接

解答
我的数据可能和题目有些出入 但是不影响

SELECT * 
FROM employee2;

之前做过分组排序的问题

SELECT E.`DepartmentId`, E.`NAME`, E.`Salary`,
IF(@pre_dep=E.`DepartmentId`, @rank:=@rank+1, @rank:=1) AS rank,
@pre_dep:= E.`DepartmentId`
FROM employee2 AS E, (SELECT @rank:=0, @pre_dep:=NULL)init
ORDER BY E.`DepartmentId`, E.`Salary` DESC;

但我们注意到1部门的第三工资为70000应该有两名 所以这里需要同分同名的排序 所以需要进行一些修正。

加入一个变量pre_sal
逻辑就是当当前dep与pre_dep相同时 可能加一也可能不变 这时需要判断与前一工资是否相同 相同则rank不变 不同则加一

SELECT E.`DepartmentId`, E.`NAME`, E.`Salary`,
IF(@pre_dep=E.`DepartmentId`, IF(@pre_sal=E.`Salary`, @rank:=@rank, @rank:=@rank +1), @rank:=1) AS rank,
@pre_dep:= E.`DepartmentId`,
@pre_sal:= E.`Salary`
FROM employee2 AS E, (SELECT @rank:=0, @pre_dep:=NULL, @pre_sal:=0)init
ORDER BY E.`DepartmentId`, E.`Salary` DESC;

再两表连接选出需要的字段即可

SELECT D.`Name`, tmp.`NAME`, tmp.`Salary`
FROM (SELECT E.`DepartmentId`, E.`NAME`, E.`Salary`,
IF(@pre_dep=E.`DepartmentId`, IF(@pre_sal=E.`Salary`, @rank:=@rank, @rank:=@rank +1), @rank:=1) AS rank,
@pre_dep:= E.`DepartmentId`,
@pre_sal:= E.`Salary`
FROM employee2 AS E, (SELECT @rank:=0, @pre_dep:=NULL, @pre_sal:=0)init
ORDER BY E.`DepartmentId`, E.`Salary` DESC) AS tmp
JOIN department AS D
ON tmp.`DepartmentId` = D.`Id`
WHERE tmp.rank <=3

本地可以运行出正确结果 结果在Leetcode无法提交。。。 服了

SELECT dpTable.Name AS Department, Employee, Salary
FROM
(SELECT Name AS Employee, Salary, DepartmentId, 
@rank := IF(@preDepartmentId = DepartmentId, IF(@preSalary = Salary, @rank + 0, @rank + 1), 1) AS SalaryRank,
@preDepartmentId := DepartmentId, @preSalary := Salary
FROM Employee, (SELECT @preDepartmentId := NULL, @preSalary := NULL, @rank := 0) AS Init
ORDER BY DepartmentId, Salary DESC) AS RankTable 
INNER JOIN Department AS dpTable ON RankTable.DepartmentId = dpTable.Id
WHERE SalaryRank <= 3;

别的方法
1.方法一
先用两表连接

SELECT E.`NAME`, E.`Salary`, D.`Name`
FROM employee2 AS E
JOIN department AS D
ON E.`DepartmentId` = D.`Id`

然后对其进行筛选 选取部门相同的大于其工资个数的人数 若小于等于2则就是前三名

SELECT E.`NAME`, E.`Salary`, D.`Name`
FROM employee2 AS E
JOIN department AS D
ON E.`DepartmentId` = D.`Id`
WHERE(
SELECT COUNT(DISTINCT E1.`Salary`)
FROM employee2 AS E1
WHERE E1.`DepartmentId` = E.`DepartmentId` AND E.`Salary` < E1.`Salary`) <= 2;

感觉好强啊。。。

2.方法二
先找出每个部门薪水第三高的薪水A。每个人的薪水只要大于等于A,就是所要结果。

SELECT *
FROM Employee2 e1
LEFT JOIN Employee2 e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary)
LEFT JOIN Employee2 e3 ON(e2.DepartmentId=e3.DepartmentId AND e2.Salary>e3.Salary)

注意:上面的小于号的顺序,e1.Salary>e2.Salary>e3.Salary。这个顺序非常得重要。

得出这样的结果:


从结果中发现,求第三高的薪水,只能在e3.Salary上求max。且要处理 e3.Salary 为null的情况。如果在 e1.Salary 上求max,得到的一定是每个部门的最高薪水。因此left join 左边的表的所有元组必然在结果中。

用CASE WHEN END子句,对null字段进行处理。

在这里当值为NULL时,将其替换为0。

SELECT e1.DepartmentId, 
CASE 
    WHEN MAX(e3.salary) IS NULL THEN 0 
    ELSE MAX(e3.salary) 
END AS max_salary
FROM Employee2 e1
LEFT JOIN Employee2 e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary)
LEFT JOIN Employee2 e3 ON(e2.DepartmentId=e3.DepartmentId AND e2.Salary>e3.Salary)
GROUP BY e1.DepartmentId

两表连接得到最后的结果

SELECT D.name AS `Department`,E.name AS `Employee`,E.Salary
FROM Employee2 AS E
JOIN Department AS D ON (E.departmentid = D.id)
JOIN (
    SELECT e1.DepartmentId, CASE WHEN MAX(e3.salary) IS NULL THEN 0 ELSE MAX(e3.salary) END AS m
    FROM Employee2 e1
    LEFT JOIN Employee2 e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary)
    LEFT JOIN Employee2 e3 ON(e2.DepartmentId=e3.DepartmentId AND e2.Salary>e3.Salary)
    GROUP BY e1.DepartmentId
) AS F ON (E.departmentid = F.departmentid AND E.salary >= F.m)

这个方法求第三高的薪水需要连接三个表且需要处理NULL 也不方便推广到N个的情况

下面尝试用定义变量的方式做一下这个问题
先对每个部门的薪资进行排序

SELECT E.salary, E.departmentId, 
IF(E.departmentId = @pre_dep, @rank:=@rank+1, @rank:=1) AS ranak,
@pre_dep:= E.departmentId
FROM employee2 AS E, (SELECT @rank:=0, @pre_dep:=NULL) AS inti
ORDER BY E.departmentId, E.salary DESC;

取出每个部门第三高的工资
由于有的部门可能只有两个人 所以最好是选排名小于等于3中的最小值作为每个部门薪资第三的薪资

SELECT tmp.departmentId, MIN(tmp.salary) AS min_sal
FROM (SELECT E.salary, E.departmentId, 
IF(E.departmentId = @pre_dep, @rank:=@rank+1, @rank:=1) AS rank,
@pre_dep:= E.departmentId
FROM employee2 AS E, (SELECT @rank:=0, @pre_dep:=NULL) AS inti
ORDER BY E.departmentId, E.salary DESC) AS tmp 
WHERE tmp.rank <=3
GROUP BY tmp.departmentId;

两表连接

SELECT dep.`Name` AS `Department`,EE.`NAME` AS `Employee`, EE.`Salary`
FROM employee2 AS EE
JOIN department AS dep
ON EE.`DepartmentId` = dep.`Id`
JOIN (SELECT tmp.departmentId, MIN(tmp.salary) AS min_sal
FROM (SELECT E.salary, E.departmentId, 
IF(E.departmentId = @pre_dep, @rank:=@rank+1, @rank:=1) AS rank,
@pre_dep:= E.departmentId
FROM employee2 AS E, (SELECT @rank:=0, @pre_dep:=NULL) AS inti
ORDER BY E.departmentId, E.salary DESC) AS tmp 
WHERE tmp.rank <=3
GROUP BY tmp.departmentId) tmp
ON tmp.departmentId = EE.`DepartmentId` AND EE.`Salary` >= tmp.min_sal 

你可能感兴趣的:(Leetcode185.部门工资前三高的所有员工(困难))