Employee
表包含所有员工信息,每个员工有对应的 Id,此外还有一列部门 Id。
创建表和数据:
Create table If Not Exists Employee (Idint, Name varchar(255), Salary int, DepartmentId int); Create table If Not Exists Department (Idint, Name varchar(255)); Truncate table Employee; insert into Employee (Id, Name, Salary,DepartmentId) values ('1', 'Joe', '70000', '1'); insert into Employee (Id, Name, Salary,DepartmentId) values ('2', 'Henry', '80000', '2'); insert into Employee (Id, Name, Salary,DepartmentId) values ('3', 'Sam', '60000', '2'); insert into Employee (Id, Name, Salary,DepartmentId) values ('4', 'Max', '90000', '1'); insert into Employee (Id, Name, Salary,DepartmentId) values ('5', 'Janet', '69000', '1'); insert into Employee (Id, Name, Salary,DepartmentId) values ('6', 'Randy', '85000', '1'); Truncate table Department; insert into Department (Id, Name) values('1', 'IT'); insert into Department (Id, Name) values('2', 'Sales');
解法:
1.判断每个人A是不是在这三批人中的一个。找出同一部门种比A薪水高的薪水种数N。用子查询完成。如果N<3,那么A属于这三批人。
select D.name as Department,E.name as Employee,E.Salary from Employee as E join Department as D on (E.departmentid = D.id) where ( select count(distinct E1.salary) from Employee as E1 where E1.departmentid = E.departmentid and E1.salary > E.salary ) <3
2.先找出每个部门薪水第三高的薪水A。每个人的薪水只要大于等于A,他肯定在这三批人中
SELECT * FROM Employee e1 LEFT JOIN Employee e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary) LEFT JOIN Employee e3 ON(e2.DepartmentId=e3.DepartmentId AND e2.Salary>e3.Salary);
+------+-------+--------+--------------+------+-------+--------+--------------+------+-------+--------+--------------+ | Id | Name | Salary | DepartmentId | Id | Name | Salary | DepartmentId | Id | Name | Salary | DepartmentId | +------+-------+--------+--------------+------+-------+--------+--------------+------+-------+--------+--------------+ | 4 | Max | 90000 | 1 | 1 | Joe | 85000 | 1 | 5 | Janet | 69000 | 1 | | 4 | Max | 90000 | 1 | 6 | Randy | 85000 | 1 | 5 | Janet | 69000 | 1 | | 1 | Joe | 85000 | 1 | 7 | Will | 70000 | 1 | 5 | Janet | 69000 | 1 | | 4 | Max | 90000 | 1 | 7 | Will | 70000 | 1 | 5 | Janet | 69000 | 1 | | 6 | Randy | 85000 | 1 | 7 | Will | 70000 | 1 | 5 | Janet | 69000 | 1 | | 4 | Max | 90000 | 1 | 1 | Joe | 85000 | 1 | 7 | Will | 70000 | 1 | | 4 | Max | 90000 | 1 | 6 | Randy | 85000 | 1 | 7 | Will | 70000 | 1 | | 2 | Henry | 80000 | 2 | 3 | Sam | 60000 | 2 | NULL | NULL | NULL | NULL | | 1 | Joe | 85000 | 1 | 5 | Janet | 69000 | 1 | NULL | NULL | NULL | NULL | | 4 | Max | 90000 | 1 | 5 | Janet | 69000 | 1 | NULL | NULL | NULL | NULL | | 6 | Randy | 85000 | 1 | 5 | Janet | 69000 | 1 | NULL | NULL | NULL | NULL | | 7 | Will | 70000 | 1 | 5 | Janet | 69000 | 1 | NULL | NULL | NULL | NULL | | 3 | Sam | 60000 | 2 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | | 5 | Janet | 69000 | 1 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | +------+-------+--------+--------------+------+-------+--------+--------------+------+-------+--------+--------------+
从结果中发现,求第三高的薪水,只能在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 Employee e1 LEFT JOIN Employee e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary) LEFT JOIN Employee e3 ON(e2.DepartmentId=e3.DepartmentId AND e2.Salary>e3.Salary) GROUP BY e1.DepartmentId
上面说顺序很重要,那么如果将上面的小于号的顺序,改为e1.Salary<e2.Salary<e3.Salary。 再求e1.Salary的max,得出的值一定时每个部门最高的薪水,而不是第三高的薪水。因为left join最左边的表的元组一定全部都在!
将上面的结果与员工表和部门表,再连接起来,得出结果。
SELECT D.name AS Department,E.name AS Employee,E.Salary FROM Employee 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 Employee e1 LEFT JOIN Employee e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary) LEFT JOIN Employee 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)
3.延续解法二的思路。 应用用户变量,求每个部门第三高薪水。
定义三个用户变量:@pre_salary——上一行的薪水;@pre_deptid——上一行的部门id;@salary_cnt——第几种薪水。
SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0;
其初始值如下,构成了一个表。
+--------------------+--------------------+----------------+ | @pre_salary:= NULL | @pre_deptid:= NULL | @salary_cnt:=0 | +--------------------+--------------------+----------------+ | NULL | NULL | 0 | +--------------------+--------------------+----------------+
将这样的表命名为A。
( SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0 ) AS A
先看将员工表与表A叉积。
select * FROM Employee t, ( SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0 ) AS A
结果为
+------+-------+--------+--------------+--------------------+--------------------+----------------+ | Id | Name | Salary | DepartmentId | @pre_salary:= NULL | @pre_deptid:= NULL | @salary_cnt:=0 | +------+-------+--------+--------------+--------------------+--------------------+----------------+ | 1 | Joe | 85000 | 1 | NULL | NULL | 0 | | 2 | Henry | 80000 | 2 | NULL | NULL | 0 | | 3 | Sam | 60000 | 2 | NULL | NULL | 0 | | 4 | Max | 90000 | 1 | NULL | NULL | 0 | | 5 | Janet | 69000 | 1 | NULL | NULL | 0 | | 6 | Randy | 85000 | 1 | NULL | NULL | 0 | | 7 | Will | 70000 | 1 | NULL | NULL | 0 | +------+-------+--------+--------------+--------------------+--------------------+----------------+
但这样的结果,对找每个部门的不同薪水没有帮助。要对结果按部门id递增排序,再按薪水降序。
select * FROM Employee t, ( SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0 ) AS A ORDER BY t.DepartmentId, t.Salary DESC
排序后
+------+-------+--------+--------------+--------------------+--------------------+----------------+ | Id | Name | Salary | DepartmentId | @pre_salary:= NULL | @pre_deptid:= NULL | @salary_cnt:=0 | +------+-------+--------+--------------+--------------------+--------------------+----------------+ | 4 | Max | 90000 | 1 | NULL | NULL | 0 | | 1 | Joe | 85000 | 1 | NULL | NULL | 0 | | 6 | Randy | 85000 | 1 | NULL | NULL | 0 | | 7 | Will | 70000 | 1 | NULL | NULL | 0 | | 5 | Janet | 69000 | 1 | NULL | NULL | 0 | | 2 | Henry | 80000 | 2 | NULL | NULL | 0 | | 3 | Sam | 60000 | 2 | NULL | NULL | 0 | +------+-------+--------+--------------+--------------------+--------------------+----------------+
从这个结果中找出,每个部门的不同薪水个数。
对结果中的每行,执行下述逻辑,计数。
if (当前行的salary = @pre_salary and 当前行的departmentid = @pre_deptid) { //相同的薪水 @salary_cnt 不变 } else if(当前行的departmentid = @pre_deptid) { //不同的薪水 @salary_cnt = @salary_cnt + 1 } else { //不同的部门,计数重新从1开始 @salary_cnt = 1 } //更新pre_salary和pre_deptid @pre_salary = 当前行的salary @pre_deptid = 当前行的departmentid
将此逻辑翻译为SQL代码,结果命令为表B
( SELECT @salary_cnt:= IF(t.Salary = @pre_salary AND t.DepartmentId = @pre_deptid, @salary_cnt, IF(t.DepartmentId = @pre_deptid, @salary_cnt + 1, 1) ) AS `cnt` ,@pre_salary := t.Salary AS `salary` ,@pre_deptid := t.DepartmentId AS `deptid` FROM Employee t, ( SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0 ) AS A ORDER BY t.DepartmentId, t.Salary DESC ) AS B
结果为:
+------+--------+--------+ | cnt | salary | deptid | +------+--------+--------+ | 1 | 90000 | 1 | | 2 | 85000 | 1 | | 2 | 85000 | 1 | | 3 | 70000 | 1 | | 4 | 69000 | 1 | | 1 | 80000 | 2 | | 2 | 60000 | 2 | +------+--------+--------+
从表B中,选出每个部门中前三种薪水,取最小的薪水。结果命名为C。
( SELECT B.deptid,MIN(B.salary) AS `salary` FROM ( SELECT @salary_cnt:= IF(t.Salary = @pre_salary AND t.DepartmentId = @pre_deptid, @salary_cnt, IF(t.DepartmentId = @pre_deptid, @salary_cnt + 1, 1) ) AS `cnt` ,@pre_salary := t.Salary AS `salary` ,@pre_deptid := t.DepartmentId AS `deptid` FROM Employee t, ( SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0 ) AS A ORDER BY t.DepartmentId, t.Salary DESC ) AS B WHERE B.cnt < 4 GROUP BY B.deptid ) AS C
结果为:
+--------+--------+ | deptid | salary | +--------+--------+ | 1 | 70000 | | 2 | 60000 | +--------+--------+
再将表C与员工表和部门表连接,所有薪水大于等于C.salary都取出来。
SELECT D.name AS `Department`,E.name AS `Employee`,E.Salary FROM Employee AS E JOIN Department AS D ON (E.departmentid = D.id) JOIN ( SELECT B.deptid,MIN(B.salary) AS `salary` FROM ( SELECT @salary_cnt:= IF(t.Salary = @pre_salary AND t.DepartmentId = @pre_deptid, @salary_cnt, IF(t.DepartmentId = @pre_deptid, @salary_cnt + 1, 1) ) AS `cnt` ,@pre_salary := t.Salary AS `salary` ,@pre_deptid := t.DepartmentId AS `deptid` FROM Employee t, ( SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0 ) AS A ORDER BY t.DepartmentId, t.Salary DESC ) AS B WHERE B.cnt < 4 GROUP BY B.deptid ) AS C ON (D.id = C.deptid AND E.salary >= C.salary)
4.计算每个员工的薪水,在其所在部门的薪水中的排名。最后取出每个部门中排名前三的员工。内在的逻辑与解法三殊途同归,但是具体的方法不同。
先取出每个部门不同薪水,并按部门id升序,薪水降序。结果命名为表A。
( SELECT DepartmentId,Salary FROM Employee GROUP BY DepartmentId,Salary ORDER BY DepartmentId,Salary DESC ) AS A
结果为:
+--------------+--------+ | DepartmentId | Salary | +--------------+--------+ | 1 | 90000 | | 1 | 85000 | | 1 | 70000 | | 1 | 69000 | | 2 | 80000 | | 2 | 60000 | +--------------+--------+
在表A上求出每个薪水的排名。也是借助于用户变量:@pre_deptid——上一行的部门id,@rank——此行的排名。
(SELECT @pre_deptid:=null, @rank:=0) AS B
叉积A和B,执行下面的逻辑计算@rank。
if (@pre_deptid = 当前行的departmentid) { //同一部门 @rank = @rank + 1 } else { //不同部门。@rank重置为1 @rank=1 }
逻辑转换为:
CASE WHEN @pre_deptid = DepartmentId THEN @rank:= @rank + 1 WHEN @pre_deptid := DepartmentId THEN @rank:= 1 END AS `rank`
薪水排名逻辑,结果命名为
( SELECT A.DepartmentId, A.Salary, CASE WHEN @pre_deptid = DepartmentId THEN @rank:= @rank + 1 WHEN @pre_deptid := DepartmentId THEN @rank:= 1 END AS `rank` FROM (SELECT @pre_deptid:=null, @rank:=0) AS B, ( SELECT DepartmentId,Salary FROM Employee GROUP BY DepartmentId,Salary ORDER BY DepartmentId,Salary DESC ) AS A ) AS C
结果为
+--------------+--------+------+ | DepartmentId | Salary | rank | +--------------+--------+------+ | 1 | 90000 | 1 | | 1 | 85000 | 2 | | 1 | 70000 | 3 | | 1 | 69000 | 4 | | 2 | 80000 | 1 | | 2 | 60000 | 2 | +--------------+--------+------+
再将表C与员工表和部门表连接,取出排名小于等于3的员工。
SELECT D.Name as Department, E.NAME AS Employee, E.Salary FROM ( SELECT A.DepartmentId, A.Salary, CASE WHEN @pre_deptid = DepartmentId THEN @rank:= @rank + 1 WHEN @pre_deptid := DepartmentId THEN @rank:= 1 END AS `rank` FROM (SELECT @pre_deptid:=null, @rank:=0) AS B, ( SELECT DepartmentId,Salary FROM Employee GROUP BY DepartmentId,Salary ORDER BY DepartmentId,Salary DESC ) AS A ) AS C INNER JOIN Department AS D ON C.DepartmentId = D.Id INNER JOIN Employee AS E ON C.DepartmentId = E.DepartmentId AND C.Salary = E.Salary AND C.rank <= 3
从结果中发现,求第三高的薪水,只能在e3.Salary上求max。且要处理 e3.Salary 为null的情况。如果在 e1.Salary 上求max,得到的一定是每个部门的最高薪水。因此left join 左边的表的所有元组必然在结果中。
用CASE WHEN END子句,对null字段进行处理。
在这里当值为NULL时,将其替换为0。