分析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 相关文章推荐
mybatis使用oracle进行添加数据的方法
Apr 27 Oracle
zabbix agent2 监控oracle数据库的方法
May 13 Oracle
Oracle11g R2 安装教程完整版
Jun 04 Oracle
C#连接ORACLE出现乱码问题的解决方法
Oct 05 Oracle
使用Oracle命令进行数据库备份与还原
Dec 06 Oracle
oracle重置序列从0开始递增1
Feb 28 Oracle
Oracle 多表查询基本语法实例
Apr 18 Oracle
在Oracle表中进行关键词搜索的过程
Jun 10 Oracle
Oracle 11g数据库使用expdp每周进行数据备份并上传到备份服务器
Jun 28 Oracle
Oracle查看表空间使用率以及爆满解决方案详解
Jul 23 Oracle
详解SQL的窗口函数
排查并解决Oracle sysaux表空间异常增长
Oracle使用别名的好处
Oracle 多表查询基本语法实例
Apr 18 #Oracle
Lakehouse数据湖并发控制陷阱分析
Oracle数据库中通用的函数实例详解
详细聊聊Oracle表碎片对性能有多大的影响
Mar 19 #Oracle
You might like
php 大数据量及海量数据处理算法总结
2011/05/07 PHP
PHP empty函数报错解决办法
2014/03/06 PHP
一个php生成16位随机数的代码(两种方法)
2014/09/16 PHP
初窥JQuery(一)jquery选择符 必备知识点
2010/11/25 Javascript
基于jQuery的试卷自动排版系统实现代码
2011/01/06 Javascript
jQuery 阴影插件代码分享
2012/01/09 Javascript
jQuery实用函数用法总结
2014/08/29 Javascript
jQuery中die()方法用法实例
2015/01/19 Javascript
jQuery给动态添加的元素绑定事件的方法
2015/03/09 Javascript
详解JavaScript UTC时间转换方法
2016/01/07 Javascript
parabola.js抛物线与加入购物车效果的示例代码
2017/10/25 Javascript
vue 清空input标签 中file的值操作
2020/07/21 Javascript
python实现网站的模拟登录
2016/01/04 Python
Python随机读取文件实现实例
2017/05/25 Python
Python对字符串实现去重操作的方法示例
2017/08/11 Python
Python简单实现的代理服务器端口映射功能示例
2018/04/08 Python
python初学者,用python实现基本的学生管理系统(python3)代码实例
2019/04/10 Python
FFrpc python客户端lib使用解析
2019/08/24 Python
Python标准库itertools的使用方法
2020/01/17 Python
python绘制趋势图的示例
2020/09/17 Python
python在地图上画比例的实例详解
2020/11/13 Python
微信浏览器左上角返回按钮拦截功能
2017/11/21 HTML / CSS
曼联官方网上商店:Manchester United Direct
2017/07/28 全球购物
自荐信结尾
2013/10/27 职场文书
质检部部长职责
2013/12/16 职场文书
委托书怎样写
2014/08/30 职场文书
小学毕业典礼演讲稿
2014/09/09 职场文书
大学生万能检讨书范例
2014/10/04 职场文书
县长“四风”对照检查材料思想汇报
2014/10/05 职场文书
收入证明申请书
2015/06/12 职场文书
小学生安全教育主题班会
2015/08/12 职场文书
小程序wx.getUserProfile接口的具体使用
2021/06/02 Javascript
python flappy bird小游戏分步实现流程
2022/02/15 Python
Node.js实现爬取网站图片的示例代码
2022/04/04 NodeJs
MySQL 数据库范式化设计理论
2022/04/22 MySQL
Linux在两个服务器直接传文件的操作方法
2022/08/05 Servers