分析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更换为MySQL遇到的问题及解决
May 21 Oracle
Oracle11g r2 卸载干净重装的详细教程(亲测有效已重装过)
Jun 04 Oracle
RPM包方式安装Oracle21c的方法详解
Aug 23 Oracle
关于Oracle12C默认用户名system密码不正确的解决方案
Oct 16 Oracle
详解Oracle数据库中自带的所有表结构(sql代码)
Nov 20 Oracle
使用Oracle命令进行数据库备份与还原
Dec 06 Oracle
分析SQL窗口函数之取值窗口函数
Apr 21 Oracle
Oracle用户管理及赋权
Apr 24 Oracle
解决Oracle数据库用户密码过期
May 11 Oracle
oracle设置密码复杂度及设置超时退出的功能
Jun 28 Oracle
Oracle中日期的使用方法实例
Jul 07 Oracle
详解SQL的窗口函数
排查并解决Oracle sysaux表空间异常增长
Oracle使用别名的好处
Oracle 多表查询基本语法实例
Apr 18 #Oracle
Lakehouse数据湖并发控制陷阱分析
Oracle数据库中通用的函数实例详解
详细聊聊Oracle表碎片对性能有多大的影响
Mar 19 #Oracle
You might like
PHP的面向对象编程
2006/10/09 PHP
判断是否为指定长度内字符串的php函数
2010/02/16 PHP
Firefox outerHTML实现代码
2009/06/04 Javascript
Javascript绝句欣赏 一些经典的js代码
2012/02/22 Javascript
JS判定是否原生方法
2013/07/22 Javascript
浅析jQuery1.8的几个小变化
2013/12/10 Javascript
javascript制作loading动画效果 loading效果
2014/01/14 Javascript
JavaScript中两个字符串的匹配
2016/06/08 Javascript
JavaScript作用域示例详解
2016/07/07 Javascript
jQuery实现的表头固定效果实例【附完整demo源码下载】
2016/08/01 Javascript
AngularJS constant和value区别详解
2017/02/28 Javascript
JS简单判断滚动条的滚动方向实现方法
2017/04/28 Javascript
bootstrap3-dialog-master模态框使用详解
2017/08/22 Javascript
jQuery实现的表格前端排序功能示例
2017/09/18 jQuery
JavaScript多线程运行库Nexus.js详解
2017/12/22 Javascript
详解layui中的树形关于取值传值问题
2018/01/16 Javascript
Less 安装及基本用法
2018/05/05 Javascript
layui实现给某一列加点击事件
2019/10/26 Javascript
关于vue-cli3打包代码后白屏的解决方案
2020/09/02 Javascript
python映射列表实例分析
2015/01/26 Python
python创建和删除目录的方法
2015/04/29 Python
Python 专题六 局部变量、全局变量global、导入模块变量
2017/03/20 Python
python 三种方法提取pdf中的图片
2021/02/07 Python
利用CSS3动画实现圆圈由小变大向外扩散的效果实例
2018/09/10 HTML / CSS
HTMl5的存储方式sessionStorage和localStorage详解
2014/03/18 HTML / CSS
Redbubble法国:由独立艺术家设计的独特产品
2019/01/08 全球购物
机械系毕业生求职信
2014/05/28 职场文书
教师考核评语大全
2014/12/31 职场文书
同学聚会通知书
2015/04/20 职场文书
工伤认定行政答辩状
2015/05/22 职场文书
2016年五四青年节校园广播稿
2015/12/17 职场文书
2016年万圣节家长开放日活动总结
2016/04/05 职场文书
描述鲁迅的名言整理,一生受用
2019/08/08 职场文书
nginx对http请求处理的各个阶段详析
2021/03/31 Servers
python中的装饰器该如何使用
2021/06/18 Python
抖音动画片,皮皮虾,《治愈系》动画在用这首REMIX作为背景音乐,Anak ,The last world with you完整版
2022/03/16 杂记