Here are info I have: Group Name /YYYYMM / Contracts. Now I need the last 12 months churn rate and SQL runs efficiently.
I wrote this , but not working properly:
SELECT GROUP_NAME,ID, YRMO,SUM(FLOAT(CTC)) AS CTC_MOS,
MAX(CASE WHEN YRMO = VARCHAR_FORMAT(DATE(TO_DATE(@ENR_END_INC,'YYYYMM'))- 12 MONTHS, 'YYYYMM') THEN VARCHAR_FORMAT(DATE(TO_DATE(@ENR_END_INC,'YYYYMM'))- 12 MONTHS, 'YYYYMM') END) AS YRMO_PRIOR,
SUM(CASE WHEN YRMO = VARCHAR_FORMAT(DATE(TO_DATE(@ENR_END_INC,'YYYYMM'))- 12 MONTHS, 'YYYYMM') THEN CTC END) AS CTC_PRIOR
FROM TABLE1
below is sample data if needed:
GP YRMO CTC
B1 202011 8
B1 202012 10
B1 202101 11
B1 202102 11
B1 202103 11
B1 202104 11
B1 202105 12
B1 202106 12
B1 202107 12
B1 202108 12
B1 202109 12
B1 202110 12
B1 202111 12
B1 202112 11
B1 202201 11
B1 202202 11
B1 202203 11
B1 202204 13
B1 202205 14
B1 202206 15
B1 202207 22
B1 202208 23
B1 202209 23
B1 202210 23
B1 202211 24
B1 202311 27
The solution without JOIN.
WITH T (GP, YRMO, CTC) AS
(
VALUES
('B1', 202011, 8)
, ('B1', 202012, 10)
, ('B1', 202111, 12)
, ('B1', 202112, 11)
, ('B1', 202211, 24)
, ('B1', 202212, 0)
)
SELECT
T.*
, COALESCE (100 * (CTC_C - CTC_P) / NULLIF (CTC_P, 0), 100)
AS RATE
FROM
(
SELECT
GP, YRMO
, CTC AS CTC_C
, YRMO - 100 AS YRMO_P
, COALESCE (LAG (CTC) OVER (PARTITION BY GP, MOD (YRMO, 100) ORDER BY YRMO), 0)
AS CTC_P
FROM T
) T
ORDER BY GP, YRMO
GP | YRMO | CTC_C | YRMO_P | CTC_P | RATE |
---|---|---|---|---|---|
B1 | 202011 | 8 | 201911 | 0 | 100 |
B1 | 202012 | 10 | 201912 | 0 | 100 |
B1 | 202111 | 12 | 202011 | 8 | 50 |
B1 | 202112 | 11 | 202012 | 10 | 10 |
B1 | 202211 | 24 | 202111 | 12 | 100 |
B1 | 202212 | 0 | 202112 | 11 | -100 |
Update:
For 6-month you may use the following expressions:
, INT (TO_CHAR (TO_DATE (VARCHAR (YRMO) || '01', 'YYYYMMDD') - 6 MONTH, 'YYYYMM'))
AS YRMO_P
, COALESCE
(
MAX (CTC) OVER
(
PARTITION BY GP
ORDER BY MOD (YRMO, 100) + (YRMO / 100) * 12
RANGE BETWEEN 6 PRECEDING AND 6 PRECEDING
)
, 0
) AS CTC_P
The idea is to enumerate months sequentially without gaps with the ORDER BY
expression to use RANGE
inside MAX () OVER ()
.