1.普及一下概念
環比 = 2018年10月/2018年09月(同一時期內不同時間段的比較)
同比 = 2018年10月/2017年10月(不同時期內相同時間段的比較)
環比增長率 = (2018年10月-2018年09月)/2018年09月
同比增長率 = (2018年10月-2017年10月)/2017年10月
2.關鍵sql 解釋
(1)分析函數
lead(params,m,n) 以params為目標向下m位取數,當取不到時默認為 n,
lag(params,m,n) 以params為目標向上m位取數,當取不到時默認為 n
over(order by...) 相當於 order by...
partition by ... 根據參數最為分割,將相同的先分組到一塊 ( 可以不要 )
同比環比概念和公式
同比: 同比一般情況下是今年第n月與去年第n月比
環比: 表示連續2個統計周期(比如連續兩月)內的量的變化比。
同比增長率=(本期數-同期數)/|同期數|×100%
環比增長率=(本期數-上期數)/上期數×100%
需求:
計算2020年01月到2020年2月, 每個月的本期值和環比和同比率
數據庫 表和數據准備
CREATE TABLE test (
"ID" NUMBER NOT NULL ,
"VALUE" NUMBER ,
"YEARMONTH" VARCHAR2(6 CHAR) ,
CONSTRAINT "SYS_C0086930" PRIMARY KEY ("ID")
)
INSERT INTO "test"("ID", "VALUE", "YEARMONTH") VALUES ('1', '100', '202002');
INSERT INTO "test"("ID", "VALUE", "YEARMONTH") VALUES ('2', '80', '202001');
INSERT INTO "test"("ID", "VALUE", "YEARMONTH") VALUES ('3', '120', '201912');
INSERT INTO "test"("ID", "VALUE", "YEARMONTH") VALUES ('4', '142', '201911');
INSERT INTO "test"("ID", "VALUE", "YEARMONTH") VALUES ('5', '70', '201910');
INSERT INTO "test"("ID", "VALUE", "YEARMONTH") VALUES ('6', '51', '201901');
OVER函數: 先對數據先進行分組和排序. 如: 這次測試,我們用日期進行排序,保證了LAG函數向上取offset行,能取到
對應的環比和同比的日期的值
再簡紹一下,如何獲取一個時間段內連續的日期
這樣保證了如果數據庫中有些日期沒有數據時,可以給一個默認值,使LAG和LEAD函數可以獲取到正確的行數
SELECT AA.monthlist, NVL(BB.CAP,0) CAP FROM -- 獲取需要查詢的時間范圍內 連續月份的 數據值
(SELECT
TO_CHAR( ADD_MONTHS( TO_DATE( '201901', 'yyyyMM' ), ROWNUM - 1 ), 'yyyyMM' ) AS monthlist
FROM
DUAL CONNECT BY ROWNUM <= months_between(
to_date( '202001', 'yyyyMM' ),
to_date( '201901', 'yyyyMM' )) + 1
思路:
第一步: 將需要的業務數據計算出來. 也就是將我們數據庫中有的數據,按照日期分組.
注意: 在這里我們從業務數據表中獲取數據的時間范圍,並不是我們需要求的2020年01月到2020年2月的數據,我們想要計算得到同比值那就必須得到最早開始時間(2020年1月)向前推12月的日期,即2019年1月. 這樣我們才能
同比增長率=(本期數2020年1月 - 同期數2019年1月 )/ 同期數2019年1月 ×100% .
環比值所需要的數據(2019年12月)已經在范圍內
SELECT YEARMONTH, NVL(SUM(VALUE), 0) VALUE FROM "test"
WHERE YEARMONTH >= 201901 AND YEARMONTH <= 202002
GROUP BY YEARMONTH ORDER BY YEARMONTH
第二步: 用我們上面簡紹的獲取連續月份的sql和上一步我們計算出來的業務數據連表, 並與連續月份的臨時表為主表.
這樣我們就可以得到,我們需要的時間范圍只能的所有日期和它的值
注意: ①獲取連續日期的sql的時間范圍和上一步的相同,都必須包含到同比值
SELECT AA.YEARMONTH, NVL(BB.VALUE, 0) VALUE FROM
(SELECT
TO_CHAR( ADD_MONTHS( TO_DATE( '201901', 'yyyyMM' ), ROWNUM - 1 ), 'yyyyMM' ) AS YEARMONTH
FROM DUAL
CONNECT BY ROWNUM <= months_between( to_date( '202002', 'yyyyMM' ), to_date( '201901', 'yyyyMM' )) + 1) AA
LEFT JOIN
(
SELECT YEARMONTH, NVL(SUM(VALUE), 0) VALUE FROM "test"
WHERE YEARMONTH >= 201901 AND YEARMONTH <= 202002
GROUP BY YEARMONTH ORDER BY YEARMONTH
) BB
ON AA.YEARMONTH = BB.YEARMONTH ORDER BY AA.YEARMONTH
第三步: 使用LAG或LEAD函數,使現值,環比值(month-on-month)和同比值(year-on-year)成為一行數據.
注意: 我們這里使用的LAG**(exp_str,offset,default)**函數, 它向上取offset行的值exp_str,如果沒有就是default.
SELECT
CC.YEARMONTH,
CC.VALUE,
LAG ( CC.VALUE, 1, 0 ) OVER ( ORDER BY CC.YEARMONTH ) AS monthOnMonth,
LAG ( CC.VALUE, 12, 0 ) OVER ( ORDER BY CC.YEARMONTH ) AS yearOnYear
FROM (
SELECT AA.YEARMONTH, NVL(BB.VALUE, 0) VALUE FROM
(SELECT
TO_CHAR( ADD_MONTHS( TO_DATE( '201901', 'yyyyMM' ), ROWNUM - 1 ), 'yyyyMM' ) AS YEARMONTH
FROM DUAL
CONNECT BY ROWNUM <= months_between( to_date( '202002', 'yyyyMM' ), to_date( '201901', 'yyyyMM' )) + 1) AA
LEFT JOIN
(
SELECT YEARMONTH, NVL(SUM(VALUE), 0) VALUE FROM "test"
WHERE YEARMONTH >= 201901 AND YEARMONTH <= 202002
GROUP BY YEARMONTH ORDER BY YEARMONTH
) BB
ON AA.YEARMONTH = BB.YEARMONTH ORDER BY AA.YEARMONTH
) CC
第四步: 計算出環比和同比的增長率
按照公式
同比增長率=(本期數-同期數)/|同期數|×100%
環比增長率=(本期數-上期數)/上期數×100%
注意: ① 我們在上一步中可以看到,我們的環比和同比值有可能為0, 但是根據公式,環比和同比值需要做被除數,而被除數又不能等於0,所以我們需要使用DECODE函數,如何值為0,環比和同比的增長率就為0
② 在整個sql的最后面我們要加一個時間范圍條件. 因為我們之前計算的都是根據最大日期范圍計算的. 但是我們所需要的僅僅是 2020年1月到2020年2月的數據,所以加上時間范圍條件.只獲取我們所需的數據.
SELECT
DD.YEARMONTH,
DD.VALUE,
TO_CHAR(NVL(ROUND(DECODE(DD.monthOnMonth,0,100,(DD.VALUE - DD.monthOnMonth )/DD.monthOnMonth * 100),2), 0), 'fm9999999990.00') || '%' 環比增長率,
TO_CHAR(NVL(ROUND(DECODE(DD.yearOnYear,0,100,(DD.VALUE - DD.yearOnYear)/DD.yearOnYear * 100),2), 0),'fm9999999990.00') || '%' 同比增長率
FROM (
SELECT
CC.YEARMONTH,
CC.VALUE,
LAG ( CC.VALUE, 1, 0 ) OVER ( ORDER BY CC.YEARMONTH ) AS monthOnMonth,
LAG ( CC.VALUE, 12, 0 ) OVER ( ORDER BY CC.YEARMONTH ) AS yearOnYear
FROM (
SELECT AA.YEARMONTH, NVL(BB.VALUE, 0) VALUE FROM
(SELECT
TO_CHAR( ADD_MONTHS( TO_DATE( '201901', 'yyyyMM' ), ROWNUM - 1 ), 'yyyyMM' ) AS YEARMONTH
FROM DUAL
CONNECT BY ROWNUM <= months_between( to_date( '202002', 'yyyyMM' ), to_date( '201901', 'yyyyMM' )) + 1) AA
LEFT JOIN
(
SELECT YEARMONTH, NVL(SUM(VALUE), 0) VALUE FROM "test"
WHERE YEARMONTH >= 201901 AND YEARMONTH <= 202002
GROUP BY YEARMONTH ORDER BY YEARMONTH
) BB
ON AA.YEARMONTH = BB.YEARMONTH ORDER BY AA.YEARMONTH
) CC
) DD WHERE DD.YEARMONTH >= 202001 AND DD.YEARMONTH <=202002
👆 就是最終的推導出的sql,該sql還可以優化和簡化, sql優化和簡化不是本篇的重點,如有需要請根據實際情況處理.
————————————————
原文鏈接:https://blog.csdn.net/qq_35976271/article/details/107157769