窗口函数 - Window Functions
- 1. 定义
- 1.1 窗口函数
- 1.2 语法格式
- 2. 分类
- 2.1 序号函数
- 2.1.1 row_number()
- 2.1.2 rank()
- 2.1.3 dense_rank()
- 2.2.4 示例
- 2.2 分布函数
- 2.2.1 percent_rank()
- 2.2.2 cume_dist()
- 2.3 前后函数
- 2.3.1 lag(expr, n, default)
- 2.3.2 lead(expr, n, default)
- 2.3.3 示例
- 2.4 头尾函数
- 2.4.1 first_value(expr)
- 2.4.2 last_value(expr)
- 2.4.3 示例
- 2.5 其他函数
- 2.5.1 nth_value(expr, n)
- 2.5.2 ntile(n)
- 2.5.3 示例
- 2.6 聚合类窗口函数
1. 定义
1.1 窗口函数
官网地址:https://dev.mysql.com/doc/refman/8.0/en/window-functions.html
窗口可以理解为记录集合
,窗口函数就是在满足某种条件的记录集合上执行的特殊函数。
即:每条记录都要在此窗口内执行函数
。
-
静态窗口:每条记录都要在此窗口内执行函数,窗口大小都是固定的。
-
动态窗口:不同的记录对应着不同的窗口,这种动态变化的窗口叫滑动窗口。
窗口函数
也称为 OLAP(Online Anallytical Processing)函数,意思是对数据库数据进行实时分析处理。窗口函数就是为了实现 OLAP 而添加的标准 SQL 功能。
窗口函数
对一组查询行执行类似聚合的操作。然而,聚合操作将查询行分组为单个结果行,而窗口函数为每个查询行生成一个结果:
-
发生函数计算的行称为当前行。
-
与对其进行函数计算的当前行相关的查询行构成当前行的窗口。
1.2 语法格式
函数名(字段名) over(子句);
over 括号内若不写,则意味着窗口函数基于满足 where 条件的所有行进行计算;
若括号内不为空,则支持以下语法来设置窗口:
函数名(字段名) over(partition by <要分列的组> order by <要排序的列> rows between <数据范围>)
数据范围:
# 取本行和前面两行
rows between 2 preceding and current row
# 取本行和之前所有的行
rows between unbounded preceding and current row
# 取本行和之后所有的行
rows between current row and unbounded following
# 从前面三行和下面一行,总共五行
rows between 3 preceding and 1 following
# 当 order by 后面没有 rows between 时,窗口规范默认是取本行和之前所有的行
# 当 order by 和 rows between 都没有时,窗口规范默认是分组下所有行 (rows between unbounded preceding and unbounded following)
2. 分类
2.1 序号函数
序号函数是按照一定的分组规则对每一组的数据排序并创建一个序号列。
2.1.1 row_number()
单纯的对每一组数据编号,进行顺序显示。
2.1.2 rank()
排序每一组的某一字段, 同等级同序号前后不连续。
对序号进行并列排序,指定字段数值相同(同一等级),则会产生相同序号记录,且产生序号间隙。
rank 函数没有参数,但需要指定按照那个字段进行排名,所以使用 rank 函数必须用 order by 参数
,order by 的排序字段就是排名字段。
2.1.3 dense_rank()
排序每一组的某一字段, 同等级同序号前后也连续。
对序号进行并列排序,指定字段数值相同(同一等级)则会产生相同序号记录,且产生序号间隙。
2.2.4 示例
# 查询显示列中一组值的每个成员的排名值 val
mysql> SELECT
val,
ROW_NUMBER() OVER w AS 'row_number',
RANK() OVER w AS 'rank',
DENSE_RANK() OVER w AS 'dense_rank'
FROM numbers
WINDOW w AS (ORDER BY val);
+------+------------+------+------------+
| val | row_number | rank | dense_rank |
+------+------------+------+------------+
| 1 | 1 | 1 | 1 |
| 1 | 2 | 1 | 1 |
| 2 | 3 | 3 | 2 |
| 3 | 4 | 4 | 3 |
| 3 | 5 | 4 | 3 |
| 3 | 6 | 4 | 3 |
| 4 | 7 | 7 | 4 |
| 4 | 8 | 7 | 4 |
| 5 | 9 | 9 | 5 |
+------+------------+------+------------+
2.2 分布函数
分布函数有:percent_rank()、cume_dist()
2.2.1 percent_rank()
计算分区或结果集中行的百分位数排名。
每行按照公式 (rank-1)/ (rows-1)
进行计算。其中,rank 为 RANK() 函数产生的序号,rows 为当前窗口(当前组)的总行数。
2.2.2 cume_dist()
累积分布值,<= 当前 rank 值的行数 / 分组内总行数,取值范围为 0~1。
应用场景:查询小于等于当前成绩(score)的比例。
示例:
mysql> SELECT
val,
ROW_NUMBER() OVER w AS 'row_number',
CUME_DIST() OVER w AS 'cume_dist',
PERCENT_RANK() OVER w AS 'percent_rank'
FROM numbers
WINDOW w AS (ORDER BY val);
+------+------------+--------------------+--------------+
| val | row_number | cume_dist | percent_rank |
+------+------------+--------------------+--------------+
| 1 | 1 | 0.2222222222222222 | 0 |
| 1 | 2 | 0.2222222222222222 | 0 |
| 2 | 3 | 0.3333333333333333 | 0.25 |
| 3 | 4 | 0.6666666666666666 | 0.375 |
| 3 | 5 | 0.6666666666666666 | 0.375 |
| 3 | 6 | 0.6666666666666666 | 0.375 |
| 4 | 7 | 0.8888888888888888 | 0.75 |
| 4 | 8 | 0.8888888888888888 | 0.75 |
| 5 | 9 | 1 | 1 |
+------+------------+--------------------+--------------+
2.3 前后函数
2.3.1 lag(expr, n, default)
返回当前行前 n 行
的 expr 值。n 如果没有这样的行,则返回值为 default。如果缺少 n 或 default,则默认值分别为 1 或 NULL。
n 必须是非负整数。如果 n 为 0, expr 则为当前行的值。
从 MySQL 8.0.22 开始,n 不能 NULL。此外,它必须是1 到 2的63次方 的整数。
2.3.2 lead(expr, n, default)
返回当前行后 n 行
的 expr 值。n 如果没有这样的行,则返回值为 default。如果缺少 n 或 default,则默认值分别为 1 或 NULL。
n 必须是非负整数。如果 n 为 0, expr 则为当前行的值。
从 MySQL 8.0.22 开始,n 不能 NULL。此外,它必须是1 到 2的63次方 的整数。
2.3.3 示例
mysql> SELECT
t, val,
LAG(val) OVER w AS 'lag',
LEAD(val) OVER w AS 'lead',
val - LAG(val) OVER w AS 'lag diff',
val - LEAD(val) OVER w AS 'lead diff'
FROM series
WINDOW w AS (ORDER BY t);
+----------+------+------+------+----------+-----------+
| t | val | lag | lead | lag diff | lead diff |
+----------+------+------+------+----------+-----------+
| 12:00:00 | 100 | NULL | 125 | NULL | -25 |
| 13:00:00 | 125 | 100 | 132 | 25 | -7 |
| 14:00:00 | 132 | 125 | 145 | 7 | -13 |
| 15:00:00 | 145 | 132 | 140 | 13 | 5 |
| 16:00:00 | 140 | 145 | 150 | -5 | -10 |
| 17:00:00 | 150 | 140 | 200 | 10 | -50 |
| 18:00:00 | 200 | 150 | NULL | 50 | NULL |
+----------+------+------+------+----------+-----------+
2.4 头尾函数
2.4.1 first_value(expr)
first_value 取分组内排序后,截止到当前行,第一个值。
2.4.2 last_value(expr)
last_value 取分组内排序后,截止到当前行,最后一个值。
2.4.3 示例
mysql> SELECT
time, subject, val,
FIRST_VALUE(val) OVER w AS 'first',
LAST_VALUE(val) OVER w AS 'last',
NTH_VALUE(val, 2) OVER w AS 'second',
NTH_VALUE(val, 4) OVER w AS 'fourth'
FROM observations
WINDOW w AS (PARTITION BY subject ORDER BY time
ROWS UNBOUNDED PRECEDING);
+----------+---------+------+-------+------+--------+--------+
| time | subject | val | first | last | second | fourth |
+----------+---------+------+-------+------+--------+--------+
| 07:00:00 | st113 | 10 | 10 | 10 | NULL | NULL |
| 07:15:00 | st113 | 9 | 10 | 9 | 9 | NULL |
| 07:30:00 | st113 | 25 | 10 | 25 | 9 | NULL |
| 07:45:00 | st113 | 20 | 10 | 20 | 9 | 20 |
| 07:00:00 | xh458 | 0 | 0 | 0 | NULL | NULL |
| 07:15:00 | xh458 | 10 | 0 | 10 | 10 | NULL |
| 07:30:00 | xh458 | 5 | 0 | 5 | 10 | NULL |
| 07:45:00 | xh458 | 30 | 0 | 30 | 10 | 30 |
| 08:00:00 | xh458 | 25 | 0 | 25 | 10 | 30 |
+----------+---------+------+-------+------+--------+--------+
2.5 其他函数
2.5.1 nth_value(expr, n)
nth_value 函数返回第 n 行的 expr 值,没有则返回 null。
2.5.2 ntile(n)
ntile 函数将分区中的有序数据分为 n 个桶,记录桶编号。
2.5.3 示例
mysql> SELECT
val,
ROW_NUMBER() OVER w AS 'row_number',
NTILE(2) OVER w AS 'ntile2',
NTILE(4) OVER w AS 'ntile4'
FROM numbers
WINDOW w AS (ORDER BY val);
+------+------------+--------+--------+
| val | row_number | ntile2 | ntile4 |
+------+------------+--------+--------+
| 1 | 1 | 1 | 1 |
| 1 | 2 | 1 | 1 |
| 2 | 3 | 1 | 1 |
| 3 | 4 | 1 | 2 |
| 3 | 5 | 1 | 2 |
| 3 | 6 | 2 | 3 |
| 4 | 7 | 2 | 3 |
| 4 | 8 | 2 | 4 |
| 5 | 9 | 2 | 4 |
+------+------------+--------+--------+
2.6 聚合类窗口函数
sum() over( partion by <要分列的组> order by <要排序的列> rows between <数据范围> )
count() over( partion by <要分列的组> order by <要排序的列> rows between <数据范围> )
avg() over( partion by <要分列的组> order by <要排序的列> rows between <数据范围> )
max() over( partion by <要分列的组> order by <要排序的列> rows between <数据范围> )
min() over( partion by <要分列的组> order by <要排序的列> rows between <数据范围> )