Employee
表包含全部員工信息,每一個員工有對應的 Id,此外還有一列部門 Id。html
建立表和數據:mysql
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');
解法:sql
1.判斷每一個人A是否是在這三批人中的一個。找出同一部門種比A薪水高的薪水種數N。用子查詢完成。若是N<3,那麼A屬於這三批人。spa
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 左邊的表的全部元組必然在結果中。3d
用CASE WHEN END子句,對null字段進行處理。code
在這裏當值爲NULL時,將其替換爲0。htm
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最左邊的表的元組必定所有都在!blog
將上面的結果與員工表和部門表,再鏈接起來,得出結果。排序
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。