目录
一,简介
二,窗口函数的两种写法
三,窗口表达式语法
1,PARTITION BY子句
2,ORDER BY子句
3,frame子句
四,窗口函数介绍
1,序号函数:row_number(),rank(),dense_rank()
2,分布函数:percent_rank() / cume_dist()
3,前后函数:lead() / lag()
4,头尾函数:first_val() / last_val()
5,其他函数:nth_value() / nfile()
6,原生聚合函数
MySQL8.0引入的窗口函数,可以比较方便的实现一些分析和统计功能,这些功能不用窗口函数也能实现,不过实现的sql可能会比较复杂。
什么是窗口函数
窗口函数引入的其实不只是几个函数,而是一套完整的语法,窗口函数是此语法中的一部分。
语法:
窗口函数 over 窗口表达式。
over是窗口函数语法的关键字。
从语法上来看,窗口函数的使用实际上分为两部分:窗口函数和窗口。
窗口函数在sql中往往使用在查询结果中,返回一列值,就像查询一个字段一样。
窗口
从数据结构上来看,要使用窗口函数,首先我们要有一个窗口,所谓窗口,个人理解是一个数据集,数据集的内容可以类比group by分组之后的一组数据,只不过在使用了窗口函数的sql中,每一行记录都有一个自己的窗口,每行记录所对应的窗口,内容类比group by分组后该行所在的那组。当然这里不是用group by关键字分组了,是在窗口表达式里用专门的语法分组。
窗口函数
定义好了窗口,我们就可以使用窗口函数来对窗口里的数据进行汇总或分析,得到我们想要的那个数据。
MySQL8.0新增了一大堆窗口函数:
另外,MySQL原有的一些聚合函数也可以用在窗口函数的语法中,当做窗口函数使用,比如sum(),avg(),max(),min(),count()等,其实这也很好理解,这些聚合函数本来就是基于分组来操作的,和窗口函数基于窗口来操作其实差不多。
窗口函数和聚合函数相同点和不同点
窗口函数和聚合函数都是基于分组的,窗口函数在窗口表达式中分组,聚合函数在group by语法中分组。
聚合函数会导致返回的行数被汇总和减少,每组一行。窗口函数不影响返回值的行数,原来有几行,用了窗口函数也还是几行,多一个字段而已。
聚合函数可以作为窗口函数在窗口函数的语法中使用。
1,over关键字后直接定义窗口:
select
row_number() over (partition by user_no order by amount desc),
order_id
from order_tab;
2,over关键字后使用窗口别名:
select
row_number() over w,
order_id
from order_tab
WINDOW w AS (partition by user_no order by amount desc);
此时w就是窗口的别名。
下面具体看一下窗口表达式的语法。
窗口表达式中可以写三个子句:
PARTITION BY子句是用来分组的,从字面上来看,应该叫分区。理解上可以类比group by。
某行记录所在的分区就是这行对应的窗口。一个窗口可以包含多行,就像group by分的组一样。
使用的语法是:
PARTITION BY
[{, …}]
其中的expression表达式,可以是列名,也可以是某个计算结果。如果是列名,就代表简单的按照列的值分区。
可以支持按照多个表达式或列来分区。
根据每行记录的值,可以确定每行属于哪个分区,也就是哪个窗口。
order by子句是排序用的,决定了属于同一个窗口的行记录的排序方式。语法很简单:
ORDER BY
[ASC|DESC] [{, …}]
和MySQL中原有的order by关键字意思差不多。原有的order by关键字是所有查询结果一起排序,窗口函数的order by子句是对某个窗口中的行记录进行排序。
frame子句定义了窗口中的一个子窗口,子集。这个稍微麻烦点,语法是:
frame_unit {
| }
frame_unit有两种选择,ROWS和RANGE,ROWS代表按起止位置标识子集,RANGE代表按起止值标识子集。
frame_start标识子窗口的开始位置,结束位置默认是当前行。
frame_between是用BETWEEN AND关键字来标识子窗口的起止位置。
可以标识位置或值的语法有:
举例:
rows BETWEEN 1 PRECEDING AND 1 FOLLOWING
上面的表达式代表窗口范围前一行到后一行,一共三行记录。
rows UNBOUNDED FOLLOWING
上面的表达式代表窗口范围是当前行到分区中的最后一行。
rows BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
上面的表达式代表窗口范围是当前分区中所有行,整个frame子句都不写的时候也是这个效果。
序号函数的作用是显示分区中每行的行号。
三者的区别在于,当排序字段相同时,三个函数的处理结果不同:
分布函数得到的是一个百分比,计算公式用到rank()函数。
percent_rank()
实际计算公式:(rank()-1)/(rows-1)
其中rank()是rank()函数得到的结果,rows是分区行数。
这个函数想要表达的功能好像不是特别好理解,强行解释一下就是这样的:如果用一条线段表示分区的列表集合,此函数得到的就是每行在线段中的起始位置。
cume_dist()
实际计算公式:rank()/rows
这个函数的含义比较好理解一点,就是小于等于自己排名的行占总行数的比例。
lead (expr,n)代表当前行前面第n行记录的expr表达式(或字段值)。
lag(expr,n)代表当前行后面的第n行记录的expr表达式(或字段值)。
比如lead (add_time,1)就代表当前行前面一行的add_time字段。
这两个函数是基于排序的。
first_val(expr)代表分区第一行记录的expr表达式(或字段值)。
last_val (expr)代表分区最后一行记录的expr表达式(或字段值)。
这两个函数是基于排序的。
nth_value(expr,n)代表区间第n条记录的expr表达式(或字段值)。
比如:nth_value(amount,2)代表区间第二行的amount字段。
nfile(n)代表把区间列分为n个组,返回组号。
注意:
这个分组只能是尽量平均,但有时候不能完全平均,比如分区内有7条数据,用nfile(3)分三组,记录的组号就会是:1,1,1,2,2,3,3。
比如sum(),avg(),max(),min(),count()等聚合函数,起的作用和他们之前的功能基本相同。
完
MySQL8.0全部学习笔记:
MySQL8.0新特性学习笔记(一):binlog复制策略优化
MySQL8.0新特性学习笔记(二):窗口函数
MySQL8.0新特性学习笔记(三):直方图
MySQL8.0新特性学习笔记(四):Hash Join
MySQL8.0新特性学习笔记(五):JSON格式简介和JSON函数详解
MySQL8.0新特性学习笔记(六):新特性介绍