窗口函数在统计类的需求中很常见,稍微复杂一点的查询需求就有可能用到它,使用窗口函数可以极大的简化我们的 SQL 语句。像 Oracle、SQL Server 这些数据库在较早的版本就支持窗口函数了,MySQL 直到 8.0 版本后才支持它。
一般来说涉及复杂的分组内问题如: 分组排名(取TOPn),分阶段统计(分阶段最大最小平均等)
都可以用到窗口函数
窗口函数需要注意以下两点:
over(partition by id)
时,出来的结果会按这个id字段排在一起(最终是否聚合要看外层有没有group by id)可以看到over()里面可以进行partition分区(就是分组的效果)、order by可以排序、后面有个RANGE字段放在第三点讲,这个RANGE或者ROW会影响over的作用效果
select distinct user_id,
first_value(knowledge_name)
over (partition by user_id order by score RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as last,
last_value(knowledge_name)
over (partition by user_id order by score RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as best
from tb_math_knowledge_point
同样的我们还可以对常见的SUM()函数接一个over()
SELECT
*,
SUM(quantity) OVER (PARTITION BY product ORDER BY quantity ROWS CURRENT ROW) AS sum1,
SUM(quantity) OVER (PARTITION BY product ORDER BY quantity RANGE CURRENT ROW) AS sum2,
SUM(quantity) OVER (PARTITION BY product RANGE CURRENT ROW) AS sum3
由此应该可以感觉到,这个over()
就是所谓的窗口,我们可以在over()
中控制滑动窗口的范围,从而取到不同的值
窗口函数的over()中有三个参数:分别是
在这一点的demo中会详细解释,本demo参考了https://blog.csdn.net/georgelee954/article/details/105763302?spm=1001.2014.3001.5506
在over()中,frame也是有默认值(默认范围)的
根据是否有partition by、order by决定不同的默认值
窗口范围是:所有行,这里即是sum对所有行进行求和
SELECT
*,
SUM(quantity) OVER () AS sum1
FROM sql_store.product_order;
窗口范围:第一个分区 到 所在行的分区,这里一般都不会这样单独用,深究意义不大
窗口范围:该行的整个分区,注意与上面的区别,如果用partition限定了分区,则扫描到哪一行,就取那行对应的整个分区
上面的这个demo只在over()中指定了partition和order,因此无法自由控制滑动窗口的范围
这个fream参数其实叫框架,我这里为了方便理解直接写为了滑窗
滑窗的筛选单位分为两种:{ROWS
| RANGE
},默认值为RANGE
所谓筛选单位,就是:被视为同一个单位的几个行数据,一起进入窗口函数,并共享最终的唯一结果
我们关于如下脚本,有3种不同的情况:
SUM(quantity) OVER (PARTITION BY product ORDER BY quantity ROWS CURRENT ROW) AS sum1
sum1中,使用ROWS作为基本单位,所以对每一行进行单独计算,sum(current row)统计值就是quantity本身
sum2中,使用RANGE作为基本单位,apple 的第二、第三个订单数量都为10,所以第二、第三行视为一个单位
sum3中,使用RANGE作为基本单位,并且没有ORDER BY,所以每个分区为一个单位
个人认为跟上面的3.1滑窗筛选单位ROWS
和RANGE
属于一个包含关系,单位可以选择ROWS行、RANGE范围,都可以筛选一定的范围内,但比较死板,不够灵活,3.2就是这个升级版,足够灵活
我们可以自由的进行组合,来达到自定义分组(分区)内
滑窗范围的效果
因此我们可以通过这种方式统计:
一般来说用后者,前者是只指定滑窗起点,终点是默认值
后者是起点终点都指定,最为灵活,也最为实用
仅指定开始行(或区域),则结束范围为默认值,即当前行(或区域)
SELECT
*,
SUM(quantity) OVER
(PARTITION BY product
ORDER BY quantity
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS sum
FROM sql_store.product_order
UNBOUNDED PRECEDING
)和frame_end(CUREENT ROW
),即框架范围为第一行至当前行,实现了累计求和的效果。