注册
做“环比”统计不同方法的优缺点分析,以及使用LEAD()函数(下偏移函数)做环比统计时遇到的“坑”和巧妙“避坑”经验分享
技术分享/ 文章详情 /

做“环比”统计不同方法的优缺点分析,以及使用LEAD()函数(下偏移函数)做环比统计时遇到的“坑”和巧妙“避坑”经验分享

liuc161639 2026/05/29 132 0 0

做“环比”统计不同方法的优缺点分析,以及使用LEAD()函数(下偏移函数)做环比统计时遇到的“坑”和巧妙“避坑”经验分享
一.需求描述
很多行业在做数据分析过程中,对数据做同比/环比分析是很常见的,比如有如下
表结构:
image.png
数据示例:
image.png
图1.1
需要统计每天的总销售额以及“环比”!

二.使用普通方法及优势和劣势分析
1.方法
如果不使用偏移函数也是可以实现的,思路就是查询每天的销售额后,再根据每天的日期去查昨天的销售额,也就是要查两遍,即查今天和昨天,sql如下
WITH cur AS
( SELECT t.SALE_DATE 销售日期,
SUM(t.SALE_AMOUNT) 销售额 ,
(SELECT NVL(SUM(SALE_AMOUNT),0) FROM “TEST”.“SALE” WHERE SALE_DATE=t.SALE_DATE-1) AS 昨天销售额 --昨天没有销售额时补0
FROM “TEST”.“SALE” t
GROUP BY t.SALE_DATE
ORDER BY t.SALE_DATE DESC
)
SELECT c.销售日期,
销售额,
CASE WHEN 昨天销售额 ==0 THEN ‘100%’ --避免分母为0
ELSE ROUND((销售额-昨天销售额)*100/昨天销售额,2) || ‘%’ END AS 环比
FROM cur c

执行结果如下图:
image.png
图2.1
sql执行计划如下:
image.png
图2.2
2.优势和劣势分析
优势>> ①语法逻辑简单(不看行数,只看逻辑),没有用到复杂的函数,初学者也能看懂; ②即使日期存在“断层”,也不影响结果。
劣势>>从图2.2可看出,sql执行计划中执行步骤很多,对性能不友好,如果数据量庞大会响应延迟。

三.使用偏移函数LEAD()方法及优势和劣势分析
1.方法
首先要搞懂LEAD()函数的作用,此函数可以对结果集向后偏移,只是偏移,不会再次做查询!
先看sql语法及偏移效果:
image.png
图3.1
整体sql如下:
WITH cur AS
( SELECT t.SALE_DATE 销售日期,
SUM(t.SALE_AMOUNT) AS 销售额,
LEAD(SUM(t.SALE_AMOUNT), 1, 0) over(ORDER BY SALE_DATE DESC) 昨天销售额
FROM “TEST”.“SALE” t
GROUP BY t.SALE_DATE
)
SELECT c.销售日期,
销售额,
CASE WHEN 昨天销售额 ==0 THEN ‘100%’ --避免分母为0
ELSE ROUND((销售额-昨天销售额)*100/昨天销售额,2) || ‘%’ END AS 环比
FROM cur c

执行结果和方法1是一样的:
image.png
图3.2
sql执行计划:
image.png
图3.3
2.优势和劣势分析
优势>>通过对比图2.2和图3.3可以看出,第二种方法的执行计划中执行步骤要少得多,当 数据量庞大时执行效率肯定会更好。
劣势>>LEAD()函数是数据库中的高等函数,且需要与over一起使用,对sql功底有一定要求。
四.使用LEAD()函数的“避坑”经验
1.可能存在的坑
通过上面方法已经知道LEAD()函数的用法以及好处,但在使用此函数时如果不仔细很容易“踩坑”,也就是当“时间不连续”时,偏移结果就乱了,比如如下图所示:
image.png
图4.1
通过图4.1可以看出,缺少了2026-05-26和2026-05-20两天的数据,结果导致2026-05-27与2026-05-25做了环比、2026-05-21与2026-05-19做了环比。这样的结果肯定是有问题的。

2.避坑方法
既然是有“时间断层”,那我们把断层的时间补上去,并且断层当天的销售额设置为0就行了!如何设置呢,手动insert两条数据进去?或者通过union all拼接两条固定数据进去?这两种方法可是可以,但如果日期跨度非常长、断层的多,且数据是持续更新的(你不知道什么时候会断层),那就不好办了。
解决这个问题,我的思路是首先把每天的数据比作一个“梯子”,每一阶梯子上都有数据,而梯子中间随机断了几阶,既然我们不知道哪几阶梯子断了,那就重新找个完整的梯子,然后将两个梯子“绑起来”,如下图,红色代表我们已有的数据。
image.png
图4.2
首先我们构造一套日期完整的数据(如图4.1的蓝色梯子),sql如下:
select TO_CHAR(sysdate-(LEVEL -1),‘YYYY-MM-DD’) 日期
CONNECT BY LEVEL <=28

执行结果如下:
image.pngimage.png
图4.3
然后我们将上面得到的结果,和原有的数据关联起来(就好比两个梯子绑起来):
select a.日期 as 销售日期,nvl(b.销售额,0) as 销售额
from
(select TO_CHAR(sysdate-(LEVEL -1),‘YYYY-MM-DD’) 日期 CONNECT BY LEVEL <=28) a
left join
(
SELECT t.SALE_DATE 销售日期,
SUM(t.SALE_AMOUNT) AS 销售额
FROM “TEST”.“SALE” t
GROUP BY t.SALE_DATE
) b on b.销售日期=a.日期
order by a.日期 desc
执行结果如下:
image.png
图4.4

然后将结果结合LEAD()函数实现环比计算,整体sql语句:
with cur as
(
select a.日期 as 销售日期,nvl(b.销售额,0) as 销售额,
LEAD(nvl(b.销售额,0), 1, 0) over(ORDER BY a.日期 DESC) 昨天销售额
from
(select TO_CHAR(sysdate-(LEVEL -1),‘YYYY-MM-DD’) 日期 CONNECT BY LEVEL <=28) a
left join
(
SELECT t.SALE_DATE 销售日期,
SUM(t.SALE_AMOUNT) AS 销售额
FROM “TEST”.“SALE” t
GROUP BY t.SALE_DATE
) b on b.销售日期=a.日期
order by a.日期 desc
)
SELECT c.销售日期,
销售额,
CASE WHEN 昨天销售额 ==0 THEN ‘100%’ --避免分母为0
ELSE ROUND((销售额-昨天销售额)*100/昨天销售额,2) || ‘%’ END AS 环比
FROM cur c
执行结果:
image.png
图4.5

优点分析:我们采用了当前时间(sysdate)做起点,也就是只需要把握好统计周期的跨度(如LEVEL<=28),不管中间哪里差、差几天,都不会影响环比计算。

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服