分析SQL窗口函数之取值窗口函数


Posted in Oracle onApril 21, 2022

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

取值窗口函数可以用于返回窗口内指定位置的数据行。常见的取值窗口函数如下:

  • LAG函数可以返回窗口内当前行之前的第N行数据。
  • LEAD函数可以返回窗口内当前行之后的第N行数据。
  • FIRST_VALUE函数可以返回窗口内第一行数据。
  • LAST_VALUE函数可以返回窗口内最后一行数据。
  • NTH_VALUE函数可以返回窗口内第N行数据。

其中,LAG函数和LEAD函数不支持动态的窗口大小,它们以整个分区作为分析的窗口。

案例分析

案例使用的示例表

下面的查询中会用到一张表,sales_monthly表中存储了商品销量信息,product表示产品名称,ym表示年月,amount表示销售金额(元)。

以下是该表中的部分数据:

分析SQL窗口函数之取值窗口函数

这个表的初始化脚本可以在文章底部获取。

1.环比分析

环比增长指的是本期数据与上期数据相比的增长,例如,产品2019年6月的销售额与2019年5月的销售额相比增加的部分。

以下语句统计了各种产品每个月的环比增长率:

SELECT s.product AS "产品", s.ym AS "年月", s.amount AS "销售额",
 ( 
    (s.amount - LAG(s.amount,1) OVER (PARTITION BY product ORDER BY s.ym))/
    LAG(s.amount,1) OVER (PARTITION BY product ORDER BY s.ym)
 ) * 100 AS "环比增长率(%)"
FROM sales_monthly s
ORDER BY s.product,s.ym

其中,LAG(amount,1)表示获取上一期的销售额,PARTITION BY选项表示按照产品分区,ORDER BY选项表示按照月份进行排序。

当前月份的销售额amount减去上一期的销售额,再除以上一期的销售额,就是环比增长率。

该查询返回的结果如下: 

分析SQL窗口函数之取值窗口函数

2018年1月是第一期,因此其环比增长率为空。

“桔子”2018年2月的环比增长率约为0.2856%((10183-10154)/10154×100),依此类推。

2.同比分析

同比增长指的是本期数据与上一年度或历史同期相比的增长,例如,产品2019年6月的销售额与2018年6月的销售额相比增加的部分。

以下语句统计了各种产品每个月的同比增长率:

SELECT s.product AS "产品", s.ym AS "年月", s.amount AS "销售额",
 ( 
    (s.amount - LAG(s.amount,12) OVER (PARTITION BY product ORDER BY s.ym))/
    LAG(s.amount,12) OVER (PARTITION BY product ORDER BY s.ym)
 ) * 100 AS "同比增长率(%)"
FROM sales_monthly s
ORDER BY s.product,s.ym

其中,LAG(amount,12)表示当前月份之前第12期的销售额,也就是去年同月份的销售额。

PARTITION BY选项表示按照产品分区,ORDER BY选项表示按照月份进行排序。

当前月份的销售额amount减去去年同期的销售额,再除以去年同期的销售额,就是同比增长率。

该查询返回的结果如下:

 

分析SQL窗口函数之取值窗口函数

2018年的12期数据都没有对应的同比增长率,“桔子”2019年1月的同比增长率约为9.3067%((11099-10154)/10154×100),依此类推。

提示:LEAD函数与LAG函数的使用方法类似,不过它的返回结果是当前行之后的第N行数据。

3.复合增长率

复合增长率是第N期的数据除以第一期的基准数据,然后开N-1次方再减去1得到的结果。

假如2018年的产品销售额为10000,2019年的产品销售额为12500,2020年的产品销售额为15000。那么这两年的复合增长率的计算方式如下:

分析SQL窗口函数之取值窗口函数

以年度为单位计算的复合增长率被称为年均复合增长率,以月度为单位计算的复合增长率被称为月均复合增长率

以下查询统计了自2018年1月以来不同产品的月均销售额复合增长率:

WITH s (product,ym,amount,first_amount,num) AS (
  SELECT m.product, m.ym, m.amount,
  FIRST_VALUE(m.amount) OVER (PARTITION BY m.product ORDER BY m.ym),
  ROW_NUMBER() OVER (PARTITION BY m.product ORDER BY m.ym)
  FROM sales_monthly m
)
 
SELECT product AS "产品", ym AS "年月",amount AS "销售额",
       (POWER( amount/first_amount, 1.0/NULLIF(num-1,0)) -1)*100 AS "月均复合增长率(%)"
FROM s
ORDER BY product, ym

首先定义了一个通用表表达式,其中FIRST_VALUE(amount)返回了第一期(201801)的销售额,ROW_NUMBER函数返回了每一期的编号。

主查询中的POWER函数用于执行开方运算,NULLIF函数用于处理第一期数据的除零错误,常量1.0用于避免由整数除法所导致的精度丢失问题。

该查询返回的结果如下:

分析SQL窗口函数之取值窗口函数

2018年1月是第一期,因此其产品月均销售额复合增长率为空。

“桔子”2018年2月的月均销售额复合增长率等于它的环比增长率,2018年3月的月均销售额复合增长率等于0.4471%,依此类推。 

4.不同产品最高和最低销售额

以下语句统计了不同产品最低销售额、最高销售额以及第三高销售额所在的月份:

SELECT product AS "产品", ym AS "年月",amount AS "销售额",
  
         FIRST_VALUE(m.ym) OVER (
           PARTITION BY m.product ORDER BY m.amount DESC
           ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
         ) AS "最高销售额月份",
         
         LAST_VALUE(m.ym) OVER (
           PARTITION BY m.product ORDER BY m.amount DESC
           ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
         ) AS "最低销售额月份",
         
         NTH_VALUE(m.ym,3) OVER (
           PARTITION BY m.product ORDER BY m.amount DESC
           ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
         ) AS "第三高销售额月份"
 
  FROM sales_monthly m
  ORDER BY product, ym;

三个窗口函数的OVER子句相同,PARTITION BY选项表示按照产品进行分区,ORDER BY选项表示按照销售额从高到低排序。

以上三个函数的默认窗口都是从分区的第一行到当前行,因此我们将窗口扩展到了整个分区。

该查询返回的结果如下: 

分析SQL窗口函数之取值窗口函数

“桔子”的最高销售额出现在2019年6月,最低销售额出现在2018年1月,第三高销售额出现在2019年4月。

示例表和脚本

-- 创建销量表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);

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


Tags in this post...

Oracle 相关文章推荐
Oracle设置DB、监听和EM开机启动的方法
Apr 25 Oracle
oracle通过存储过程上传list保存功能
May 12 Oracle
Oracle 死锁的检测查询及处理
Sep 25 Oracle
Oracle 临时表空间SQL语句的实现
Sep 25 Oracle
详解Oracle数据库中自带的所有表结构(sql代码)
Nov 20 Oracle
oracle删除超过N天数据脚本的方法
Feb 28 Oracle
Oracle 多表查询基本语法实例
Apr 18 Oracle
排查并解决Oracle sysaux表空间异常增长
Apr 20 Oracle
分析SQL窗口函数之聚合窗口函数
Apr 21 Oracle
清空 Oracle 安装记录并重新安装
Apr 26 Oracle
instantclient客户端 连接oracle数据库
Apr 26 Oracle
Oracle锁表解决方法的详细记录
Jun 05 Oracle
分析SQL窗口函数之排名窗口函数
Apr 21 #Oracle
分析SQL窗口函数之聚合窗口函数
Apr 21 #Oracle
详解SQL的窗口函数
排查并解决Oracle sysaux表空间异常增长
Oracle使用别名的好处
Oracle 多表查询基本语法实例
Apr 18 #Oracle
Lakehouse数据湖并发控制陷阱分析
You might like
西德产收音机
2021/03/01 无线电
一些使用频率比较高的php函数
2008/10/03 PHP
php常用的url处理函数总结
2014/11/19 PHP
WordPress过滤垃圾评论的几种主要方法小结
2016/07/11 PHP
PHP实现登录搜狐广告获取广告联盟数据的方法【附demo源码】
2016/10/14 PHP
php中html_entity_decode实现HTML实体转义
2018/06/13 PHP
脚本吧 - 幻宇工作室用到js,超强推荐expand.js
2006/12/23 Javascript
javascript 操作cookies及正确使用cookies的属性
2009/10/15 Javascript
Javascript 去除数组的重复元素
2010/05/04 Javascript
Jquery升级新版本后选择器的语法问题
2010/06/02 Javascript
使用jQuery轻松实现Ajax的实例代码
2010/08/16 Javascript
改变隐藏的input中value值的方法
2014/03/19 Javascript
js实现Select头像选择实时预览代码
2015/08/17 Javascript
javascript 将共享属性迁移到原型中去的实现方法
2016/08/31 Javascript
JS常见创建类的方法小结【工厂方式,构造器方式,原型方式,联合方式等】
2017/04/01 Javascript
JS 插件dropload下拉刷新、上拉加载使用小结
2017/04/13 Javascript
AngularJS中使用three.js的实例详解
2017/07/21 Javascript
微信小程序下拉刷新界面的实现
2017/09/28 Javascript
《javascript少儿编程》location术语总结
2018/05/27 Javascript
vue.js 图片上传并预览及图片更换功能的实现代码
2018/08/27 Javascript
图文详解vue框架安装步骤
2019/02/12 Javascript
ES6 async、await的基本使用方法示例
2020/06/06 Javascript
用javascript实现倒计时效果
2021/02/09 Javascript
[45:52]2018DOTA2亚洲邀请赛 4.1小组赛 A组加赛 LGD vs Liquid
2018/04/02 DOTA
python超简单解决约瑟夫环问题
2015/05/12 Python
解决Python中字符串和数字拼接报错的方法
2016/10/23 Python
python操作 hbase 数据的方法
2016/12/18 Python
Python元组操作实例分析【创建、赋值、更新、删除等】
2017/07/24 Python
python3的数据类型及数据类型转换实例详解
2019/08/20 Python
python 绘制正态曲线的示例
2020/09/24 Python
阿提哈德航空官方网站:Etihad Airways
2017/01/06 全球购物
给学校的建议书范文
2014/05/15 职场文书
环保公益策划方案
2014/08/15 职场文书
2014年财政工作总结
2014/12/10 职场文书
2019年图书室自查报告范本
2019/10/12 职场文书
win10如何快速切换窗口 win10切换窗口快捷键分享
2022/07/23 数码科技