分析SQL窗口函数之聚合窗口函数


Posted in Oracle onApril 21, 2022

关于窗口函数的基础,请看文章详解SQL的窗口函数

许多常见的聚合函数也可以作为窗口函数使用,包括AVG()、SUM()、COUNT()、MAX()以及MIN()等函数。

案例分析

案例使用的示例表

下面的查询中会用到两个表,其中sales_monthly表中存储了不同产品(苹果、香蕉、橘子)每个月的销售额情况。以下是该表中的部分数据:

分析SQL窗口函数之聚合窗口函数

transfer_log表中记录了一些银行账户的交易日志。以下是该表中的部分数据: 

分析SQL窗口函数之聚合窗口函数

该表中的字段分别表示交易日志编号、交易时间、交易发起账户、交易接收账户、交易类型以及交易金额。这两个表的初始化脚本可以在文章底部获取。

1.移动平均值

AVG函数在作为窗口函数使用时,可以用于计算随着当前行移动的窗口内数据行的平均值。

例如,以下语句用于查找不同产品每个月以及截至当前月最近3个月的平均销售额

SELECT m.product,m.ym,m.amount,
AVG(m.amount) OVER(
 PARTITION BY m.product
 ORDER BY m.ym
 ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
)
FROM sales_monthly m
ORDER BY m.product,m.ym

AVG函数OVER子句中的PARTITION BY选项表示按照产品进行分区。

ORDER BY选项表示按照月份进行排序;ROWS BETWEEN 2 PRECEDING AND CURRENT ROW表示窗口从当前行的前2行开始,直到当前行结束。该查询返回的结果如下:

分析SQL窗口函数之聚合窗口函数

对于“橘子”:

第一个月的分析窗口只有1行数据,因此平均销售额为“10154”。

第二个月的分析窗口为第1行和第2行数据,因此平均销售额为“10168.5”((10154+10183)/2)。

第三个月的分析窗口为第1行到第3行数据,因此平均销售额为“10194”((10154+10183+10245)/3)。

依此类推,直到计算完“橘子”所有月份的平均销售额,然后开始计算其他产品的平均销售额。

2.累计求和(ROW)

SUM函数作为窗口函数时,可以用于统计指定窗口内的累计值。

例如,以下语句用于查找不同产品截至当前月份的累计销售额:

SELECT m.product,m.ym,m.amount,
SUM(m.amount) OVER(
 PARTITION BY m.product
 ORDER BY m.ym
 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
FROM sales_monthly m
ORDER BY m.product,m.ym

SUM函数OVER子句中的PARTITION BY选项表示按照产品进行分区。

ORDER BY选项表示按照月份进行排序。

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW表示窗口从当前分区第1行开始,直到当前行结束。

该查询返回的结果如下:

分析SQL窗口函数之聚合窗口函数

对于“橘子”:

第一个月的分析窗口只有1行数据,因此累计销售额为“10154”。

第二个月的分析窗口为第1行和第2行数据,因此累计销售额为“20337”(10154+10183)。

第三个月的分析窗口为第1行到第3行数据,因此累计销售额为“30582”(10154+10183+10245)。

依此类推,直到计算完“橘子”所有月份的累计销售额,然后开始计算其他产品的累计销售额。

提示:对于聚合窗口函数,如果我们没有指定ORDER BY选项,默认的窗口大小就是整个分区。

如果我们指定了ORDER BY选项,默认的窗口大小就是分区的第一行到当前行。

因此,以上示例语句中的ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW选项可以省略。省略后的语句:

分析SQL窗口函数之聚合窗口函数

 执行结果与上面相同。

如果去掉ORDER BY选项,查询的窗口大小就是整个分区,如下图所示:

分析SQL窗口函数之聚合窗口函数

这时,合计值就变成了分区内所有记录的合计。

3.累计求和(RANGE)

除使用ROWS关键字以数据行为单位指定窗口的偏移量外,我们也可以使用RANGE关键字以数值为单位指定窗口的偏移量。

例如,以下语句用于查找短期之内(5天)累计转账超过100万元的账户:

SELECT log_ts,from_user,total_amount FROM (
    SELECT to_char(t.log_ts,'yyyy-mm-dd hh24:mi:ss') log_ts,t.from_user,t.amount,
      SUM(t.amount) OVER(
        PARTITION BY t.from_user
        ORDER BY t.log_ts
        RANGE INTERVAL '5' DAY PRECEDING
      ) AS total_amount
    FROM transfer_log t 
    WHERE t.type = '转账'
)
WHERE total_amount >= 1000000;

其中,SUM函数OVER子句中的RANGE选项指定了一个5天之内的时间窗口。该查询返回的结果如下:

分析SQL窗口函数之聚合窗口函数

截至2021年1月10日7时46分02秒,账户“62221234567890”在最近5天之内累计转账105万元。

示例表和脚本

-- 创建销量表sales_monthly
-- product表示产品名称,ym表示年月,amount表示销售金额(元)
CREATE TABLE sales_monthly(product VARCHAR(20), ym VARCHAR(10), amount NUMERIC(10, 2));
 
-- 生成测试数据
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201801',10159.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201802',10211.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201803',10247.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201804',10376.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201805',10400.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201806',10565.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201807',10613.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201808',10696.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201809',10751.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201810',10842.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201811',10900.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201812',10972.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201901',11155.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201902',11202.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201903',11260.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201904',11341.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201905',11459.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('苹果','201906',11560.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201801',10138.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201802',10194.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201803',10328.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201804',10322.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201805',10481.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201806',10502.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201807',10589.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201808',10681.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201809',10798.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201810',10829.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201811',10913.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201812',11056.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201901',11161.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201902',11173.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201903',11288.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201904',11408.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201905',11469.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('香蕉','201906',11528.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201801',10154.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201802',10183.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201803',10245.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201804',10325.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201805',10465.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201806',10505.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201807',10578.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201808',10680.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201809',10788.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201810',10838.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201811',10942.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201812',10988.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201901',11099.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201902',11181.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201903',11302.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201904',11327.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201905',11423.00);
INSERT INTO sales_monthly (product,ym,amount) VALUES ('桔子','201906',11524.00);
 
 
-- 创建银行交易日志表transfer_log
-- Oracle、MySQL、PostgreSQL以及SQLite
CREATE TABLE transfer_log
( log_id    INTEGER NOT NULL PRIMARY KEY, -- 交易日志编号
  log_ts    TIMESTAMP NOT NULL, -- 交易时间
  from_user VARCHAR(50) NOT NULL, -- 交易发起账号
  to_user   VARCHAR(50), -- 交易接收账号
  type      VARCHAR(10) NOT NULL, -- 交易类型
  amount    NUMERIC(10) NOT NULL -- 交易金额(元)
);
 
 
-- 生成测试数据
-- Oracle 需要执行以下ALTER语句
ALTER SESSION SET nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS';
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (1,'2021-01-02 10:31:40','62221234567890',NULL,'存款',50000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (2,'2021-01-02 10:32:15','62221234567890',NULL,'存款',100000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (3,'2021-01-03 08:14:29','62221234567890','62226666666666','转账',200000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (4,'2021-01-05 13:55:38','62221234567890','62226666666666','转账',150000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (5,'2021-01-07 20:00:31','62221234567890','62227777777777','转账',300000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (6,'2021-01-09 17:28:07','62221234567890','62227777777777','转账',500000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (7,'2021-01-10 07:46:02','62221234567890','62227777777777','转账',100000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (8,'2021-01-11 09:36:53','62221234567890',NULL,'存款',40000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (9,'2021-01-12 07:10:01','62221234567890','62228888888881','转账',10000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (10,'2021-01-12 07:11:12','62221234567890','62228888888882','转账',8000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (11,'2021-01-12 07:12:36','62221234567890','62228888888883','转账',5000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (12,'2021-01-12 07:13:55','62221234567890','62228888888884','转账',6000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (13,'2021-01-12 07:14:24','62221234567890','62228888888885','转账',7000);
INSERT INTO transfer_log (log_id,log_ts,from_user,to_user,type,amount) VALUES (14,'2021-01-21 12:11:16','62221234567890','62228888888885','转账',70000);

到此这篇关于SQL窗口函数之聚合窗口函数的使用的文章就介绍到这了!

Oracle 相关文章推荐
Oracle笔记
Apr 05 Oracle
oracle表分区的概念及操作
Apr 24 Oracle
Oracle设置DB、监听和EM开机启动的方法
Apr 25 Oracle
使用Navicat Premium工具将oracle数据库迁移到MySQL
May 27 Oracle
ORACLE查看当前账号的相关信息
Jun 18 Oracle
Oracle 死锁的检测查询及处理
Sep 25 Oracle
Oracle安装TNS_ADMIN环境变量设置参考
Nov 01 Oracle
详解Oracle数据库中自带的所有表结构(sql代码)
Nov 20 Oracle
Lakehouse数据湖并发控制陷阱分析
Mar 31 Oracle
详解SQL的窗口函数
Apr 21 Oracle
SQL试题 使用窗口函数选出连续3天登录的用户
Apr 24 Oracle
详解SQL的窗口函数
排查并解决Oracle sysaux表空间异常增长
Oracle使用别名的好处
Oracle 多表查询基本语法实例
Apr 18 #Oracle
Lakehouse数据湖并发控制陷阱分析
Oracle数据库中通用的函数实例详解
详细聊聊Oracle表碎片对性能有多大的影响
Mar 19 #Oracle
You might like
德生9700DX电路分析
2021/03/02 无线电
PHP生成静态页面详解
2006/12/05 PHP
Apache中php.ini的设置方法
2013/02/28 PHP
Laravel 实现关系模型取出需要的字段
2019/10/10 PHP
关于 byval 与 byref 的区别分析总结
2007/10/08 Javascript
基于jQuery的计算文本框字数的代码
2012/06/06 Javascript
jquery实现滑动图片自己测试的例子
2013/11/05 Javascript
Javascript快速排序算法详解
2014/12/03 Javascript
jQuery中:text选择器用法实例
2015/01/03 Javascript
D3.js进阶系列之CSV表格文件的读取详解
2017/06/06 Javascript
JavaScript原型链与继承操作实例总结
2018/08/24 Javascript
解决LayUI加上form.render()下拉框和单选以及复选框不出来的问题
2019/09/27 Javascript
BootstrapValidator验证用户名已存在(ajax)
2019/11/08 Javascript
解决vue 退出动画无效的问题
2020/08/09 Javascript
详解Vue3 Teleport 的实践及原理
2020/12/02 Vue.js
Python 元组(Tuple)操作详解
2014/03/11 Python
Python FTP文件定时自动下载实现过程解析
2019/11/12 Python
Django 自定义分页器的实现代码
2019/11/24 Python
python实现按关键字筛选日志文件
2019/12/24 Python
pytorch实现focal loss的两种方式小结
2020/01/02 Python
pytorch+lstm实现的pos示例
2020/01/14 Python
Pycharm中安装wordcloud等库失败问题及终端通过pip安装的Python库如何添加到Pycharm解释器中(推荐)
2020/05/10 Python
Python调用.net动态库实现过程解析
2020/06/05 Python
tensorflow 动态获取 BatchSzie 的大小实例
2020/06/30 Python
英国领先的男装设计师服装购物网站:Mainline Menswear
2018/02/04 全球购物
生产管理的三大手法
2013/11/11 职场文书
推广普通话标语
2014/06/27 职场文书
企业趣味活动方案
2014/08/21 职场文书
学习焦裕禄精神践行三严三实心得体会
2014/10/13 职场文书
2014大学生学生会工作总结
2014/12/19 职场文书
孔子观后感
2015/06/08 职场文书
2015年信息技术教研组工作总结
2015/07/22 职场文书
国庆放假通知怎么写
2015/07/30 职场文书
MySQL查询日期时间
2022/05/15 MySQL
SpringBoot使用ip2region获取地理位置信息的方法
2022/06/21 Java/Android
win11开机发生死循环重启怎么办?win11开机发生死循环重启解决方法
2022/08/05 数码科技