MySQL 窗口函数
一、简介
MysQL从8.o版本开始支持窗口函数。窗口函数的作用类似于在查询中对数据进行分组,不同的是,分组操作会把分组的结果聚合成一条记录,而窗口函数是将结果置于每一条数据记录中。
二、分类
窗口函数可以分为静态窗口函数
和动态窗口函数
。
- 静态窗口函数的窗口大小是固定的,不会因为记录的不同而不同;
- 动态窗口函数的窗口大小会随着记录的不同而变化。
窗口函数总体上可以分为序号函数
、分布函数
、前后函数
、首尾函数
和其他函数
,如下表:
三、语法结构
窗口函数的基本语法
<分析函数> over ([partition by <列清单>] order by <排序用列清单> [rows between 开始位置 and 结束位置])
窗口函数的语法结构
函数 over ([partition by 字段名 order by 字段名 asc|desc])
或者是:
函数 over 窗口名 ... window 窗口名 as ([partition by 字段名 order by 字段名 asc|desc])
- over 关键字指定函数窗口的范围:
- 如果省略后面括号中的内容,则窗口会包含满足where条件的所有记录,窗口函数会基于所有满足where条件的记录进行计算。
- 如果over关键字后面的括号不为空,则可以使用如下语法设置窗口。
- 窗口名:为窗口设置一个别名,用来标识窗口。
partition by
子句: 指定窗口函数按照哪些字段进行分组。分组后,窗口函数可以在每个分组中分别执行。order by
子句:指定窗口函数按照哪些字段进行排序。执行排序操作使窗口函数按照排序后的数据记录的顺序进行编号。- frame子句:为分区中的某个子集定义规则,可以用来作为滑动窗口使用。
窗口范围说明:
rows between unbounded preceding and current row
(表示从起点到当前行),常用该窗口来计算累加;range between unbounded preceding and unbounded following
(表示本组内第一行到最后一行);
(一)滑动窗口:rows & range用法
[ between and ]
rows:
表示按照行的范围进行定义框架,根据order by子句排序后,取的前n行及后n行的数据计算(与当前行的值无关,只与排序后的行号相关)。常用:rows n perceding表示从当前行到前n行(一共n+1行)range:
表示按照值的范围进行定义框架,根据order by子句排序后,指定当前行对应值的范围取值,行数不固定,只要行值在范围内,对应行都包含在内。适用于对日期、时间、数值排序分组
边界可取值(start expr & end expr) | 说明 |
---|---|
current row | 当前行 |
n preceding | 前 n 行,n 为数字, 比如 2 preceding 表示前2行 |
unbounded preceding | 开头 |
n following | 后n行,n 为数字, 比如 2 following 表示后2行 |
unbounded following | 结尾 |
range取特定日期区间 | 说明 |
---|---|
range interval 7-1 day preceding | 最近7天的值 |
range between interval 1 day preceding and interval 1 day following | 前后一天和当天的值 |
(二)函数执行顺序
在加入窗口函数的基础上SQL的执行顺序也会发生变化,具体的执行顺序如下(window就是窗口函数):
四、分类讲解
(一)序号函数
row_number()
例一:
查询 goods 数据表中每个商品分类下价格降序排列的各个商品信息
select
*,row_number() over(partition by category order by price desc) rnk
from goods g
例二:
查询 goods 数据表中每个商品分类下价格最高的3种商品信息
select * from (
select *,
row_number() over(partition by category order by price desc) as rnk
from goods
) t
where rnk <= 3;
rank()函数
使用rank()函数获取 goods 数据表中各类别的价格从高到低排序的各商品信息
select
*,rank() over(partition by category order by price desc) rnk
from goods g
dense_rank()函数
使用dense_rank()函数获取 goods 数据表中各类别的价格从高到低排序的各商品信息
select
*,dense_rank() over(partition by category order by price desc) rnk
from goods g
(二)分布函数
percent_rank()
注意:percent_rank() 等级值百分比 = (rank - 1)/ (组内rows - 1)
计算 goods 数据表中名称为"女装/女士精品”的类别下的商品的PERCENT RANK值
写法一:
select
*,rank() over(partition by category order by price desc) rk,
percent_rank() over(partition by category order by price desc) pk
from goods g
写法二:
select
*,rank() over w as rk,
percent_rank() over w as pk
from goods g
where category_id is not null window w as (partition by category_id order by price desc)
cume_dist()函数
cume_dist()累积分布值 = (<=当前rank值的行数) / 分组内总行数
CUME_DIST()函数主要用于查询小于或等于某个值的比例。
select
*,cume_dist() over(partition by category order by price asc) cd
from goods g
(三)前后函数
lag(expr,n)函数
返回分组字段的前第n个值
函数 | 功能 |
---|---|
LAG(expr, n) | 返回当前行的前n行(本组)的expr值 |
– | lag允许你在每一个分组内, 从当前行向前看n行数据 |
– | n(也叫offset)是从当前行偏移的行数,以获取值。offset必须是一个非负整数。如果offset为零,则LAG()函数计算当前行的值。如果省略 offset,则LAG()函数默认使用n=1, 向前看一个数据。 |
查询goods数据表中前一个商品价格与当前商品价格的差值。
select *,lag(price,1) OVER (partition BY category ORDER BY price) AS pre_price
FROM goods
lead(expr,n)函数
lead(expr,n)函数返回当前行的后n行的expr的值。
select
*,lead(price,1) over(partition by category order by price desc) after_rk
from goods g
(四)首尾函数
first_value(expr)函数
first_value(expr)函数返回本组内第一个expr的值
select
*,first_value(price) over(partition by category order by price desc) frk
from goods g
last_value(expr)函数
last_value(expr)函数可以返回本组内最后一个expr的值
语法结构:
在 over 子句中,
partition by
subject 将按照学科进行分区order by
grade desc 将每个分区内的行按照成绩逆序排列。range between unbounded preceding and unbounded following
定义了每个分区中与当前行相关的窗口框架,这里是从分区的第一行到最后一行。last_value(grade)
返回每行在其关联的分区的第一行的成绩(grade)。因为是按照 grade 逆序排序,于是每行中添加了一列显示最差的成绩。
要查看在每个科目中每个学生按照成绩从高到低的排序号和每个科目中的最好成绩
select
*,
last_value(grade) over (
partition by subject
order by grade desc
range between unbounded preceding and unbounded following
) last_grade
from student_grade;
(五)其他函数
nth_value(expr,n)函数
nth_value(expr,n)函数返回分组内第n个expr的值。
例一:
查询goods数据表中排名第1和第2的价格信息
select
*,
nth_value (price,1) over(
partition by category
order by price desc
range between unbounded preceding and unbounded following
) first_rk,
nth_value (price,2) over(
partition by category
order by price desc
range between unbounded preceding and unbounded following
) second_rk
from goods g
ntile(n)函数
ntile(n)函数将分区中的有序数据分为n个桶,记录桶编号
描述:
- ntile() 函数将当前行所在的分区内的所有行尽可能平均的分成指定数量的区间,并返回当前行所在的区间编号。
- 每个区间, MySQL 称之为一个排名桶。 ntile() 根据指定排序为每个桶指设定排名。
语法:
ntile(buckets)
over (
[partition by partition_column_list]
[order by order_column_list]
)
参数:
- buckets:必需的。桶的数量。桶的数量最大为此分区内的行的数量。
- partition_column_list:参与分区的列的列表。
- order_column_list:参与排序的列的列表。
例一:
将qoods表中的商品按照价格分为3组
select
*,
ntile(3) over(
partition by category
order by price desc
range between unbounded preceding and unbounded following
) nt
from goods g
五、总结
窗口函数的特点是可以分组,而且可以在分组内排序。另外,窗口函数不会因为分组而减少原表中的行数,这对我们在原表数据的基础上进行统计和排序非常有用。
关注林哥,持续更新哦!!!★,°:.☆( ̄▽ ̄)/$:.°★ 。