使用窗口函数,对查询结果进行排序,分配对应的编号
- row_number()
- rank()
- dense_rank()
row_number()
去重排序: 在每个分组内,为查询出来的每一行记录生成一个序号,依次排序且不会重复(即使结果相同,也会排出 1 2 3 名)
+------------+----------+--------+--------+
| Department | Employee | Salary | 编号 |
+------------+----------+--------+--------+
| IT | Jim | 90000 | 1 |
| IT | Max | 90000 | 2 |
| IT | li | 90000 | 3 |
| Sales | Henry | 80000 | 1 |
| Sales | pan | 95000 | 2 |
+------------+----------+--------+--------+
rank()
跳跃排序: 在每个分组内,如果有两个第一位时,接下来就是第三位
+------------+----------+--------+--------+
| Department | Employee | Salary | Rank |
+------------+----------+--------+--------+
| IT | Jim | 90000 | 1 |
| IT | Max | 90000 | 1 |
| IT | li | 80000 | 3 |
| Sales | Henry | 80000 | 1 |
| Sales | pan | 95000 | 2 |
+------------+----------+--------+--------+
dense_rank()
连续排序,在每个分组内,如果有两个第一级时,接下来仍然是第二级。
+------------+----------+--------+--------+
| Department | Employee | Salary | Rank |
+------------+----------+--------+--------+
| IT | Jim | 90000 | 1 |
| IT | Max | 90000 | 1 |
| IT | li | 80000 | 2 |
| Sales | Henry | 80000 | 1 |
| Sales | pan | 95000 | 2 |
+------------+----------+--------+--------+
语法:
dense_rank() over(partition by 分组字段 order by 排序字段 desc) as '序号命名'
注意:
使用row_number() over() 函数时,over()里的排序晚于where 、group by、order by的执行
partiton by 用于给结果集分组,如果未指定则将整个结果集作为一个分组
partiton by 与聚合函数区别:可以返回一个分组中多条记录,而聚合函数一般只有一个反应统计值的记录
举个栗子:
-- 表结构
-- Employee 表:
+----+-------+--------+--------------+
| id | name | salary | departmentId |
+----+-------+--------+--------------+
| 1 | Joe | 70000 | 1 |
| 2 | Jim | 90000 | 1 |
| 3 | Henry | 80000 | 2 |
| 4 | Sam | 60000 | 2 |
| 5 | Max | 90000 | 1 |
| 6 | li | 90000 | 1 |
| 7 | pan | 90000 | 2 |
+----+-------+--------+--------------+
-- Department 表:
+----+-------+
| id | name |
+----+-------+
| 1 | IT |
| 2 | Sales |
+----+-------+
-- sql语句:变动的是窗口函数(dense_rank()连续排序)
select
d.`name` Department, e.`name` Employee , e.salary Salary
from
(select
id,`name`,salary,departmentId,
dense_rank() over(partition by departmentId order by salary desc) as `Rank`
from Employee
) e
INNER JOIN
Department d on e.departmentId = d.ID
where e.Rank = 1
-- 执行结果(函数不同,结果集也不同)
+------------+----------+--------+--------+
| Department | Employee | Salary | Rank |
+------------+----------+--------+--------+
| IT | Jim | 90000 | 1 |
| IT | Max | 90000 | 1 |
| IT | li | 80000 | 2 |
| Sales | pan | 90000 | 1 |
| Sales | Henry | 80000 | 2 |
+------------+----------+--------+--------+
Employee
+--------------+---------+
| 列名 | 类型 |
+--------------+---------+
| id | int |
| name | varchar |
| salary | int |
| departmentId | int |
+--------------+---------+
id是此表的主键列。
departmentId是Department表中ID的外键。
此表的每一行都表示员工的ID、姓名和工资。它还包含他们所在部门的ID。
Department
+-------------+---------+
| 列名 | 类型 |
+-------------+---------+
| id | int |
| name | varchar |
+-------------+---------+
id是此表的主键列。
此表的每一行都表示一个部门的ID及其名称
输入:
Employee 表:
+----+-------+--------+--------------+
| id | name | salary | departmentId |
+----+-------+--------+--------------+
| 1 | Joe | 70000 | 1 |
| 2 | Jim | 90000 | 1 |
| 3 | Henry | 80000 | 2 |
| 4 | Sam | 60000 | 2 |
| 5 | Max | 90000 | 1 |
+----+-------+--------+--------------+
Department 表:
+----+-------+
| id | name |
+----+-------+
| 1 | IT |
| 2 | Sales |
+----+-------+
输出:
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT | Jim | 90000 |
| Sales | Henry | 80000 |
| IT | Max | 90000 |
+------------+----------+--------+
解释:Max 和 Jim 在 IT 部门的工资都是最高的,Henry 在销售部的工资最高。
Create table If Not Exists Employee (id int, name varchar(255), salary int, departmentId int);
Create table If Not Exists Department (id int, 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', 'Jim', '90000', '1');
insert into Employee (id, name, salary, departmentId) values ('3', 'Henry', '80000', '2');
insert into Employee (id, name, salary, departmentId) values ('4', 'Sam', '60000', '2');
insert into Employee (id, name, salary, departmentId) values ('5', 'Max', '90000', '1');
insert into Employee (id, name, salary, departmentId) values ('6', 'li', '18000', '3');
Truncate table Department;
insert into Department (id, name) values ('1', 'IT');
insert into Department (id, name) values ('2', 'Sales');
-- 方法一:窗口函数的方式
select
d.`name` Department, e.`name` Employee , e.salary Salary
from
(select
id,`name`,salary,departmentId,
dense_rank() over(partition by departmentId order by salary desc) as `Rank`
from Employee
) e
INNER JOIN
Department d on e.departmentId = d.ID
where e.Rank = 1
-- 方法二:内连接+分组+聚合函数的方式
SELECT
Department.name AS 'Department',
Employee.name AS 'Employee',
Salary
FROM
Employee
JOIN
Department ON Employee.DepartmentId = Department.Id
WHERE
(Employee.DepartmentId , Salary) IN
( SELECT
DepartmentId, MAX(Salary)
FROM
Employee
GROUP BY DepartmentId
)
;
-- 结果如下
+------------+----------+--------+--------+
| Department | Employee | Salary | Rank |
+------------+----------+--------+--------+
| IT | Jim | 90000 | 1 |
| IT | Max | 90000 | 1 |
| Sales | Henry | 80000 | 1 |
+------------+----------+--------+--------+
注意: MySQL8.0以后的版本才支持窗口函数