一.介绍
假设有一个学生表 student,包含以下字段:id(学号)、name(姓名)、age(年龄)、score(分数)、class_id(班级编号)。
mysql> select * from student;
+----+------+-----+--------+----------+
| id | name | age | score | calss_id |
+----+------+-----+--------+----------+
| 1 | fuck | 18 | 11.000 | 3 |
| 2 | man | 21 | 33.400 | 1 |
| 3 | yes | 33 | 91.000 | 3 |
| 4 | NO | 61 | 71.000 | 2 |
| 5 | bash | 44 | 41.500 | 1 |
| 6 | sql | 91 | 10.500 | 2 |
+----+------+-----+--------+----------+
6 rows in set (0.00 sec)
窗口函数是在 MySQL 8.0 版本及以上引入的功能。
二.sum over
使用:sum(计算字段名) over (partition by 分组字段名)
请你编写一个 SQL 查询,返回每个学生的详细信息(字段顺序和原始表的字段顺序一致),并计算每个班级的学生平均分(class_avg_score)。
mysql> select * from student;
+----+------+-----+--------+----------+
| id | name | age | score | calss_id |
+----+------+-----+--------+----------+
| 1 | fuck | 18 | 11.000 | 3 |
| 2 | man | 21 | 33.400 | 1 |
| 3 | yes | 33 | 91.000 | 3 |
| 4 | NO | 61 | 71.000 | 2 |
| 5 | bash | 44 | 41.500 | 1 |
| 6 | sql | 91 | 10.500 | 2 |
+----+------+-----+--------+----------+
6 rows in set (0.00 sec)
mysql> select *,avg(score) over (partition by calss_id) as class_avg_score from student;
+----+------+-----+--------+----------+-----------------+
| id | name | age | score | calss_id | class_avg_score |
+----+------+-----+--------+----------+-----------------+
| 2 | man | 21 | 33.400 | 1 | 37.4500000 |
| 5 | bash | 44 | 41.500 | 1 | 37.4500000 |
| 4 | NO | 61 | 71.000 | 2 | 40.7500000 |
| 6 | sql | 91 | 10.500 | 2 | 40.7500000 |
| 1 | fuck | 18 | 11.000 | 3 | 51.0000000 |
| 3 | yes | 33 | 91.000 | 3 | 51.0000000 |
+----+------+-----+--------+----------+-----------------+
6 rows in set (0.01 sec)
三.sum over order by
可以实现同组内数据的累加求和 。
使用:sum(计算字段名) over (partition by分组字段名 order by 排序字段 排序规则)
请你编写一个 SQL 查询,返回每个学生的详细信息(字段顺序和原始表的字段顺序一致),并且按照分数升序的方式累加计算每个班级的学生总分(class_sum_score)。
mysql> select * from student;
+----+------+-----+--------+----------+
| id | name | age | score | calss_id |
+----+------+-----+--------+----------+
| 1 | fuck | 18 | 11.000 | 3 |
| 2 | man | 21 | 33.400 | 1 |
| 3 | yes | 33 | 91.000 | 3 |
| 4 | NO | 61 | 71.000 | 2 |
| 5 | bash | 44 | 41.500 | 1 |
| 6 | sql | 91 | 10.500 | 2 |
+----+------+-----+--------+----------+
6 rows in set (0.00 sec)
mysql> select *,sum(score) over (partition by calss_id order by score asc) as class_sum_score from student
;
+----+------+-----+--------+----------+-----------------+
| id | name | age | score | calss_id | class_sum_score |
+----+------+-----+--------+----------+-----------------+
| 2 | man | 21 | 33.400 | 1 | 33.400 |
| 5 | bash | 44 | 41.500 | 1 | 74.900 |
| 6 | sql | 91 | 10.500 | 2 | 10.500 |
| 4 | NO | 61 | 71.000 | 2 | 81.500 |
| 1 | fuck | 18 | 11.000 | 3 | 11.000 |
| 3 | yes | 33 | 91.000 | 3 | 102.000 |
+----+------+-----+--------+----------+-----------------+
6 rows in set (0.00 sec)
四.Rank
Rank 开窗函数是 SQL 中一种用于对查询结果集中的行进行 排名 的开窗函数。它可以根据指定的列或表达式对结果集中的行进行排序,并为每一行分配一个排名。在排名过程中,相同的值将被赋予相同的排名,而不同的值将被赋予不同的排名。
使用:
RANK() OVER (
PARTITION BY 列名1, 列名2, ... -- 可选,用于指定分组列,将结果集按照指定列进行分组;
ORDER BY 列名3 [ASC|DESC], 列名4 [ASC|DESC], ... -- 用于指定排序列及排序方式,决定了计算 Rank 时的排序规则
) AS rank_column --AS rank_column 用于指定生成的 Rank 排名列的别名。
请你编写一个 SQL 查询,返回每个学生的详细信息(字段顺序和原始表的字段顺序一致),并且按照分数降序的方式计算每个班级内的学生的分数排名(ranking)。
mysql> select * from student;
+----+------+-----+--------+----------+
| id | name | age | score | calss_id |
+----+------+-----+--------+----------+
| 1 | fuck | 18 | 11.000 | 3 |
| 2 | man | 21 | 33.400 | 1 |
| 3 | yes | 33 | 91.000 | 3 |
| 4 | NO | 61 | 71.000 | 2 |
| 5 | bash | 44 | 41.500 | 1 |
| 6 | sql | 91 | 10.500 | 2 |
+----+------+-----+--------+----------+
6 rows in set (0.00 sec)
mysql> select *,rank() over (partition by calss_id order by score desc) as ranking from student;
+----+------+-----+--------+----------+---------+
| id | name | age | score | calss_id | ranking |
+----+------+-----+--------+----------+---------+
| 5 | bash | 44 | 41.500 | 1 | 1 |
| 2 | man | 21 | 33.400 | 1 | 2 |
| 4 | NO | 61 | 71.000 | 2 | 1 |
| 6 | sql | 91 | 10.500 | 2 | 2 |
| 3 | yes | 33 | 91.000 | 3 | 1 |
| 1 | fuck | 18 | 11.000 | 3 | 2 |
+----+------+-----+--------+----------+---------+
6 rows in set (0.00 sec)
五.Row_Number
Row_Number开窗函数是 SQL 中的一种用于为查询结果集中的每一行分配唯一连续排名 的开窗函数。它与之前讲到的 Rank 函数,Row_Number 函数为每一行都分配一个唯一的整数值,不管是否存在并列(相同排序值)的情况。每一行都有一个唯一的行号,从 1 开始连续递增。
使用: 与rank相同
请你编写一个 SQL 查询,返回每个学生的详细信息(字段顺序和原始表的字段顺序一致),并且按照分数降序的方式给每个班级内的学生分配一个编号(row_number)。
mysql> select * from student;
+----+------+-----+--------+----------+
| id | name | age | score | calss_id |
+----+------+-----+--------+----------+
| 1 | fuck | 18 | 11.000 | 3 |
| 2 | man | 21 | 33.400 | 1 |
| 3 | yes | 33 | 91.000 | 3 |
| 4 | NO | 61 | 71.000 | 2 |
| 5 | bash | 44 | 41.500 | 1 |
| 6 | sql | 91 | 10.500 | 2 |
| 7 | a | 38 | 11.000 | 3 |
+----+------+-----+--------+----------+
7 rows in set (0.00 sec)
mysql> select *,row_number() over (partition by calss_id order by score desc) as ow_number from student;
+----+------+-----+--------+----------+-----------+
| id | name | age | score | calss_id | ow_number |
+----+------+-----+--------+----------+-----------+
| 5 | bash | 44 | 41.500 | 1 | 1 |
| 2 | man | 21 | 33.400 | 1 | 2 |
| 4 | NO | 61 | 71.000 | 2 | 1 |
| 6 | sql | 91 | 10.500 | 2 | 2 |
| 3 | yes | 33 | 91.000 | 3 | 1 |
| 1 | fuck | 18 | 11.000 | 3 | 2 |
| 7 | a | 38 | 11.000 | 3 | 3 |
+----+------+-----+--------+----------+-----------+
7 rows in set (0.00 sec)
六.Lag 和 Lead
开窗函数 Lag 和 Lead 的作用是获取在当前行之前或之后的行的值,这两个函数通常在需要比较相邻行数据或进行时间序列分析时非常有用。
1.Lag 函数用于获取 当前行之前 的某一列的值。它可以帮助我们查看上一行的数据。
使用:lag(要获取值的列名, 向上偏移的行数, 可选参数用于指定当没有前一行时的默认值) over (partition by 分组字段名 order by 排序字段 排序规则)
2.Lead 函数用于获取 当前行之后 的某一列的值。它可以帮助我们查看下一行的数据。
使用:lag(要获取值的列名, 向下偏移的行数, 可选参数用于指定当没有后一行时的默认值) over (partition by 分组字段名 order by 排序字段 排序规则)
请你编写一个 SQL 查询,返回每个学生的详细信息(字段顺序和原始表的字段顺序一致),并且按照分数降序的方式获取每个班级内的学生的前一名学生姓名(prev_name)、后一名学生姓名(next_name)。
mysql> select * from student;
+----+------+-----+--------+----------+
| id | name | age | score | calss_id |
+----+------+-----+--------+----------+
| 1 | fuck | 18 | 11.000 | 3 |
| 2 | man | 21 | 33.400 | 1 |
| 3 | yes | 33 | 91.000 | 3 |
| 4 | NO | 61 | 71.000 | 2 |
| 5 | bash | 44 | 41.500 | 1 |
| 6 | sql | 91 | 10.500 | 2 |
| 7 | a | 38 | 11.000 | 3 |
+----+------+-----+--------+----------+
7 rows in set (0.00 sec)
mysql> select *,lag(name,1) over (partition by calss_id order by score desc) as prev_name,lead(name,1) over (partition by calss_id order by score desc) as next_name from student;
+----+------+-----+--------+----------+-----------+-----------+
| id | name | age | score | calss_id | prev_name | next_name |
+----+------+-----+--------+----------+-----------+-----------+
| 5 | bash | 44 | 41.500 | 1 | NULL | man |
| 2 | man | 21 | 33.400 | 1 | bash | NULL |
| 4 | NO | 61 | 71.000 | 2 | NULL | sql |
| 6 | sql | 91 | 10.500 | 2 | NO | NULL |
| 3 | yes | 33 | 91.000 | 3 | NULL | fuck |
| 1 | fuck | 18 | 11.000 | 3 | yes | a |
| 7 | a | 38 | 11.000 | 3 | fuck | NULL |
+----+------+-----+--------+----------+-----------+-----------+
7 rows in set (0.01 sec)