分析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 相关文章推荐
zabbix agent2 监控oracle数据库的方法
May 13 Oracle
Oracle 区块链表创建过程详解
May 15 Oracle
ORACLE数据库应用开发的三十个注意事项
Jun 07 Oracle
SQL模糊查询报:ORA-00909:参数个数无效问题的解决
Jun 21 Oracle
Oracle以逗号分隔的字符串拆分为多行数据实例详解
Jul 16 Oracle
Oracle 临时表空间SQL语句的实现
Sep 25 Oracle
oracle重置序列从0开始递增1
Feb 28 Oracle
oracle删除超过N天数据脚本的方法
Feb 28 Oracle
Oracle使用别名的好处
Apr 19 Oracle
分析SQL窗口函数之聚合窗口函数
Apr 21 Oracle
分析SQL窗口函数之取值窗口函数
Apr 21 Oracle
分析SQL窗口函数之聚合窗口函数
Apr 21 #Oracle
详解SQL的窗口函数
排查并解决Oracle sysaux表空间异常增长
Oracle使用别名的好处
Oracle 多表查询基本语法实例
Apr 18 #Oracle
Lakehouse数据湖并发控制陷阱分析
Oracle数据库中通用的函数实例详解
You might like
中高级PHP程序员应该掌握哪些技术?
2016/09/23 PHP
thinkPHP5框架自定义验证器实现方法分析
2018/06/11 PHP
prototype 1.5相关知识及他人笔记
2006/12/16 Javascript
Javascript 判断客户端浏览器类型代码
2010/03/01 Javascript
javascript 解决表单仍然提交即使监听处理函数返回false
2010/03/14 Javascript
Jquery实现弹出层分享微博插件具备动画效果
2013/04/03 Javascript
js正则表达式replace替换变量方法
2016/05/21 Javascript
AngularJS教程之环境设置
2016/08/16 Javascript
js从输入框读取内容,比较两个数字的大小方法
2017/03/13 Javascript
基于nodejs 的多页面爬虫实例代码
2017/05/31 NodeJs
javascript数组定义的几种方法
2017/10/06 Javascript
JavaScript实现音乐自动切换和轮播
2017/11/05 Javascript
JS正则表达式常见用法实例详解
2018/06/19 Javascript
微信小程序云开发之模拟后台增删改查
2019/05/16 Javascript
React-redux实现小案例(todolist)的过程
2019/09/29 Javascript
详解JavaScript中分解数字的三种方法
2021/01/05 Javascript
python实现搜索本地文件信息写入文件的方法
2016/02/22 Python
Python如何实现文本转语音
2016/08/08 Python
python学生信息管理系统(完整版)
2020/04/05 Python
详解python selenium 爬取网易云音乐歌单名
2019/03/28 Python
pyqt实现.ui文件批量转换为对应.py文件脚本
2019/06/19 Python
Python 旋转打印各种矩形的方法
2019/07/09 Python
python爬虫 execjs安装配置及使用
2019/07/30 Python
python打印直角三角形与等腰三角形实例代码
2019/10/20 Python
使用python处理题库表格并转化为word形式的实现
2020/04/14 Python
Python中logging日志记录到文件及自动分割的操作代码
2020/08/05 Python
python中通过pip安装库文件时出现“EnvironmentError: [WinError 5] 拒绝访问”的问题及解决方案
2020/08/11 Python
python zip()函数的使用示例
2020/09/23 Python
python飞机大战游戏实例讲解
2020/12/04 Python
美国马匹用品和马钉购物网站:State Line Tack
2018/08/05 全球购物
上课迟到检讨书
2014/01/19 职场文书
就业意向书
2014/07/29 职场文书
个人遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
作风整顿个人剖析材料
2014/10/06 职场文书
作弊检讨书范文
2015/05/06 职场文书
Nginx配置之实现多台服务器负载均衡
2021/08/02 Servers