Oracle version: 12c.
I have the below table:
CAT_PROD    FROM_DT     TO_DT       IMP
A1          15/01/2023  07/02/2023  100
A2          13/01/2023  16/01/2023  100
And I would like to create the below output:
CAT_PROD    RANGE_DT    IMP_RANGE   EXPLANATION
A1          202301      70,83       There are 17 days between 15th Jan (included) and 31st Jan. 17*100/24 = 70.83. 24 is the number of days between 15th Jan and 7th Feb.
A1          202302      29,17       There are 7 days between 1st Feb (included) and 7th Feb. 7*100/24 = 29.17. 24 is the number of days between 15th Jan and 7th Feb.
A2          202301      100         There are 4 days between 13th Jan (included) and 16th Jan. 4*100/4= 100. 4 is the number of days between 13th Jan and 16th Jan.
Criteria:
- IMP is prorated amongs the days of the month in which it falls.
 - RANGE_DT is the YYYYMM for each combination of days in the month.
 - The combination CAT_PROD and RANGE_DT should be unique.
 
EDIT: This is what I have tried, but it is very very slow when having more than a few rows in the original data.
WITH aux(cat_prod, startdate, enddate, imp) AS
  (SELECT 'A1' , DATE'2023-01-15' , DATE'2023-02-07' , 100 from dual
  
  UNION
  
  SELECT 'A2' , DATE'2023-01-13' , DATE'2023-01-16' , 100 from dual
  ),
  apply_cross as
  (select e.cat_prod,
    e.imp,
    enddate-startdate + 1 total_days_range,
    case
      when e.startdate > x.s_date
      then e.startdate
      else x.s_date
    end as start_date,
    case
      when e.enddate < x.e_date
      then e.enddate
      else x.e_date
    end as end_date
  from aux e cross apply
    (select trunc( e.startdate, 'mm') + (level-1) * interval '1' month         as s_date,
      trunc( e.startdate              + (level) * interval '1' month, 'mm') -1 as e_date
    from dual
      connect by level <= months_between( trunc( e.enddate, 'mm'),trunc( e.startdate, 'mm')) + 1
    ) x
  )
select ac.cat_prod,
  to_char(start_date, 'YYYYMM') month_id,
  round(imp*(end_date-start_date+1)/total_days_range, 2) imp_prorate
from apply_cross ac;