Home > Net >  Split month days onto multiple row once that row meet a limit (say each bucket limit is 90)
Split month days onto multiple row once that row meet a limit (say each bucket limit is 90)

Time:08-26

Sample Data:

date1 date2 fiscal mon days diff
22-10-21 29-10-21 Oct21 8
30-10-21 26-11-21 Nov21 28
27-11-21 24-12-21 Dec21 28
25-12-21 28-01-22 Jan22 35
29-01-22 25-02-22 Feb22 28
26-02-22 25-03-22 Mar22 28
26-03-22 29-04-22 Apr22 35
30-04-22 27-05-22 May22 28
28-05-22 24-06-22 Jun22 28
25-06-22 29-07-22 Jul22 35
30-07-22 13-08-22 Aug22 15

Required Output:

Month Oct21 Nov21 Dec21 Jan22 Feb22 Mar22 Apr22 May22 Jun22 Jul22 Aug22
Bkt90 8 28 28 26
Bkt90-180 9 28 28 25
Bkt180 10 28 28 35 15

CodePudding user response:

With some transformations it could be done like below:

  1. Your Sample Data
WITH 
    tbl_1 AS
        (
            Select To_Date('22-10-2021', 'dd-mm-yyyy') "DATE_1",    To_Date('29-10-2021', 'dd-mm-yyyy') "DATE_2",   'Oct21' "MON",   8 "DAY_DIFF" From Dual Union All
            Select To_Date('30-10-2021', 'dd-mm-yyyy') "DATE_1",    To_Date('26-11-2021', 'dd-mm-yyyy') "DATE_2",   'Nov21' "MON",  28 "DAY_DIFF" From Dual Union All
            Select To_Date('27-11-2021', 'dd-mm-yyyy') "DATE_1",    To_Date('24-12-2021', 'dd-mm-yyyy') "DATE_2",   'Dec21' "MON",  28 "DAY_DIFF" From Dual Union All
            Select To_Date('25-12-2021', 'dd-mm-yyyy') "DATE_1",    To_Date('28-01-2022', 'dd-mm-yyyy') "DATE_2",   'Jan22' "MON",  35 "DAY_DIFF" From Dual Union All
            Select To_Date('29-01-2022', 'dd-mm-yyyy') "DATE_1",    To_Date('25-05-2022', 'dd-mm-yyyy') "DATE_2",   'Feb22' "MON",  28 "DAY_DIFF" From Dual Union All
            Select To_Date('26-02-2022', 'dd-mm-yyyy') "DATE_1",    To_Date('25-03-2022', 'dd-mm-yyyy') "DATE_2",   'Mar22' "MON",  28 "DAY_DIFF" From Dual Union All
            Select To_Date('26-03-2022', 'dd-mm-yyyy') "DATE_1",    To_Date('29-04-2022', 'dd-mm-yyyy') "DATE_2",   'Apr22' "MON",  35 "DAY_DIFF" From Dual Union All
            Select To_Date('30-04-2022', 'dd-mm-yyyy') "DATE_1",    To_Date('27-05-2022', 'dd-mm-yyyy') "DATE_2",   'May22' "MON",  28 "DAY_DIFF" From Dual Union All
            Select To_Date('28-05-2022', 'dd-mm-yyyy') "DATE_1",    To_Date('24-06-2022', 'dd-mm-yyyy') "DATE_2",   'Jun22' "MON",  28 "DAY_DIFF" From Dual Union All
            Select To_Date('25-06-2022', 'dd-mm-yyyy') "DATE_1",    To_Date('29-07-2022', 'dd-mm-yyyy') "DATE_2",   'Jul22' "MON",  35 "DAY_DIFF" From Dual Union All
            Select To_Date('30-07-2022', 'dd-mm-yyyy') "DATE_1",    To_Date('13-08-2022', 'dd-mm-yyyy') "DATE_2",   'Aug22' "MON",  15 "DAY_DIFF" From Dual 
        ),
  1. First Transformation (CTE transformed)
  • 1st grouping (STEP)
  • Cumulativ Diff
  • Days Diff limitation and over the limits
    transformed AS
        (
            SELECT  
                DATE_1, 
                DATE_2, 
                MON, 
                DAY_DIFF,
                FLOOR(Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) / 90) "STEP",
                Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "CUM_DIFF",
                CASE 
                    WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 180
                    THEN DAY_DIFF - (Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 180) 
                    WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 90
                    THEN DAY_DIFF - (Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 90)
                ELSE 0
                END "DAYS_LIMIT",
                CASE 
                    WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 180
                    THEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 180
                    WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 90
                    THEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 90
                ELSE 0
                END "DAYS_OVER"
            FROM    
                tbl_1
        ),
  1. Second Transformation (CTE buckets)
    buckets AS
        (
              SELECT
                  DATE_1 "DATE_1",
                  DATE_2 "DATE_2",
                  MON "MON",
                  DAY_DIFF "DAY_DIFF",
                  CUM_DIFF "CUMM_DIFF",
                  CUM_DIFF * (STEP - FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) "CUM_DIFF_STEP",
                  STEP "STEP",
                  CASE WHEN DAYS_LIMIT <= 0 THEN DAY_DIFF ELSE DAYS_LIMIT END "DAYS_LIMIT",
                  FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) "CHG",
                  CASE 
                      WHEN CUM_DIFF * (STEP - FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) = 0 
                      THEN 0
                  ELSE
                      DAYS_OVER 
                  END "DAYS_OVER"
              FROM
                  transformed
              ORDER BY DATE_1
        ),

So far the result is:

--  DATE_1    DATE_2    MON     DAY_DIFF  CUMM_DIFF CUM_DIFF_STEP       STEP DAYS_LIMIT        CHG  DAYS_OVER
--  --------- --------- ----- ---------- ---------- ------------- ---------- ---------- ---------- ----------
--  22-OCT-21 29-OCT-21 Oct21          8          8             0          0          8          0          0 
--  30-OCT-21 26-NOV-21 Nov21         28         36             0          0         28          0          0 
--  27-NOV-21 24-DEC-21 Dec21         28         64             0          0         28          0          0 
--  25-DEC-21 28-JAN-22 Jan22         35         99            99          1         26          0          9 
--  29-JAN-22 25-MAY-22 Feb22         28        127             0          1         28          1          0 
--  26-FEB-22 25-MAR-22 Mar22         28        155             0          1         28          1          0 
--  26-MAR-22 29-APR-22 Apr22         35        190           190          2         25          1         10 
--  30-APR-22 27-MAY-22 May22         28        218             0          2         28          2          0 
--  28-MAY-22 24-JUN-22 Jun22         28        246             0          2         28          2          0 
--  25-JUN-22 29-JUL-22 Jul22         35        281           281          3         35          2        101 
--  30-JUL-22 13-AUG-22 Aug22         15        296             0          3         15          3          0
  1. Union All to create additional rows for overlapings (CTE buckets_all)
    buckets_all AS
        (
            SELECT  BKT, MON, DAYS_MON
            FROM
                (
                    SELECT
                        DATE_1 "DATE_1",
                        CASE 
                            WHEN CHG = 0 THEN 'Bkt90'
                            WHEN CHG = 1 THEN 'Bkt180'
                        ELSE
                            'Bkt180 '
                        END "BKT",
                        MON "MON",
                        DAYS_LIMIT "DAYS_MON",
                        CHG "CHG"
                    FROM
                        buckets   
                  UNION ALL
                    SELECT
                        DATE_1 "DATE_1",
                        CASE 
                            WHEN CHG = 0 THEN 'Bkt180'
                            WHEN CHG = 1 THEN 'Bkt180 '
                        ELSE
                            'xxx'
                        END "BKT",
                        MON "MON",
                        DAYS_OVER "DAYS_MON",
                        CHG   1 "CHG"
                    FROM
                        buckets 
                    WHERE
                        DAYS_OVER > 0 And
                        CHG <= 1
                    ORDER BY DATE_1, CHG
                )
        )

Now we have the result that we need for final SQL (Pivoting)

--  BKT     MON     DAYS_MON
--  ------- ----- ----------
--  Bkt90   Oct21          8 
--  Bkt90   Nov21         28 
--  Bkt90   Dec21         28 
--  Bkt90   Jan22         26 
--  Bkt180  Jan22          9 
--  Bkt180  Feb22         28 
--  Bkt180  Mar22         28 
--  Bkt180  Apr22         25 
--  Bkt180  Apr22         10 
--  Bkt180  May22         28 
--  Bkt180  Jun22         28 
--  Bkt180  Jul22         35 
--  Bkt180  Aug22         15
  1. Main SQL Pivoting the data
SELECT 
    *
FROM
    buckets_all
PIVOT
    (
        Max(DAYS_MON)
        FOR MON IN('Oct21' "Oct21", 'Nov21' "Nov21", 'Dec21' "Dec21", 'Jan22' "Jan22", 'Feb22' "Feb22", 'Mar22' "Mar22", 'Apr22' "Apr22", 'May22' "May22", 'Jun22' "Jun22", 'Jul22' "Jul22", 'Aug22' "Aug22")
    )
ORDER BY 
    CASE BKT 
        WHEN 'Bkt90' THEN 0   
        WHEN 'Bkt180' THEN 1  
        WHEN 'Bkt180 ' THEN 2
    END

R e s u l t

--  BKT          Oct21      Nov21      Dec21      Jan22      Feb22      Mar22      Apr22      May22      Jun22      Jul22      Aug22
--  ------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
--  Bkt90            8         28         28         26                                                                              
--  Bkt180                                            9         28         28         25                                             
--  Bkt180                                                                            10         28         28         35         15
  • Related