分析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 DGMGRL ORA-16603报错的解决方法(DG Broker)
Apr 06 Oracle
mybatis使用oracle进行添加数据的方法
Apr 27 Oracle
Oracle11g r2 卸载干净重装的详细教程(亲测有效已重装过)
Jun 04 Oracle
Oracle创建只读账号的详细步骤
Jun 07 Oracle
详细聊聊Oracle表碎片对性能有多大的影响
Mar 19 Oracle
Oracle数据库中通用的函数实例详解
Mar 25 Oracle
Lakehouse数据湖并发控制陷阱分析
Mar 31 Oracle
Oracle用户管理及赋权
Apr 24 Oracle
解决Oracle数据库用户密码过期
May 11 Oracle
分析SQL窗口函数之聚合窗口函数
Apr 21 #Oracle
详解SQL的窗口函数
排查并解决Oracle sysaux表空间异常增长
Oracle使用别名的好处
Oracle 多表查询基本语法实例
Apr 18 #Oracle
Lakehouse数据湖并发控制陷阱分析
Oracle数据库中通用的函数实例详解
You might like
php_screw安装使用教程(另一个PHP代码加密实现)
2014/05/29 PHP
php防止sql注入简单分析
2015/03/18 PHP
phpstorm 配置xdebug的示例代码
2019/03/31 PHP
Dojo之路:如何利用Dojo实现Drag and Drop效果
2007/04/10 Javascript
Jquery cookie操作代码
2010/03/14 Javascript
jQuery对象数据缓存Cache原理及jQuery.data方法区别介绍
2013/04/07 Javascript
单击浏览器右上角的X关闭窗口弹出提示的小例子
2013/06/12 Javascript
JS验证控制输入中英文字节长度(input、textarea等)具体实例
2013/06/21 Javascript
分享Javascript中最常用的55个经典小技巧
2013/11/29 Javascript
node.js中的console.dir方法使用说明
2014/12/10 Javascript
javascript使用prototype完成单继承
2014/12/24 Javascript
JavaScript数据结构和算法之图和图算法
2015/02/11 Javascript
javascript父子页面通讯实例详解
2015/07/17 Javascript
javascript中递归函数用法注意点
2015/07/30 Javascript
如何实现JavaScript动态加载CSS和JS文件
2020/12/28 Javascript
jquery实现左右无缝轮播图
2020/07/31 Javascript
node.js Sequelize实现单实例字段或批量自增、自减
2016/12/08 Javascript
BootStrap CSS全局样式和表格样式源码解析
2017/01/20 Javascript
nodejs实现爬取网站图片功能
2017/12/14 NodeJs
Vue 实现树形视图数据功能
2018/05/07 Javascript
原生JS封装_new函数实现new关键字的功能
2018/08/12 Javascript
详解如何给React-Router添加路由页面切换时的过渡动画
2019/04/25 Javascript
[03:12]2016完美“圣”典风云人物:单车专访
2016/12/02 DOTA
Python 中的 else详解
2016/04/23 Python
彻底搞懂Python字符编码
2018/01/23 Python
Python开发之基于模板匹配的信用卡数字识别功能
2020/01/13 Python
python numpy--数组的组合和分割实例
2020/02/24 Python
python游戏开发的五个案例分享
2020/03/09 Python
CSS3教程(3):border-color网页边框色彩
2009/04/02 HTML / CSS
什么是makefile? 如何编写makefile?
2012/08/08 面试题
Java Servlet API中forward() 与redirect()的区别
2014/04/20 面试题
灰雀教学反思
2014/04/28 职场文书
毕业生学校推荐信范文
2014/05/21 职场文书
渠道运营商合作协议书范本
2014/10/06 职场文书
2014年环保局工作总结
2014/12/11 职场文书
python实现高效的遗传算法
2021/04/07 Python