With one CTE to provide the data, and a couple of pre-conditioning CTE's
WITH data_table(start_date, end_date) as (
SELECT * from values
('2022-01-25'::date, '2022-02-04'::date),
('2018-08-31'::date, '2018-10-05'::date)
), large_range as (
SELECT row_number() over (order by null)-1 as rn
FROM table(generator(ROWCOUNT => 1000))
), pre_condition as (
SELECT
start_date
,end_date
,datediff('month', start_date, end_date) as m
FROM data_table
)
SELECT
date_trunc('month', dateadd('month', r.rn, d.start_date)) as month
,d.start_date
,d.end_date
,datediff('day', greatest(d.start_date, month), least(d.end_date, dateadd('day', -1, dateadd('month', 1, month))))+1 as days_count
FROM pre_condition as d
JOIN large_range as r ON r.rn <= d.m
ORDER BY d.start_date, month;
gives:
| MONTH |
START_DATE |
END_DATE |
DAYS_COUNT |
| 2018-08-01 |
2018-08-31 |
2018-10-05 |
1 |
| 2018-09-01 |
2018-08-31 |
2018-10-05 |
30 |
| 2018-10-01 |
2018-08-31 |
2018-10-05 |
5 |
| 2022-01-01 |
2022-01-25 |
2022-02-04 |
7 |
| 2022-02-01 |
2022-01-25 |
2022-02-04 |
4 |
And if you don't like the look for CTE's
SELECT
date_trunc('month', dateadd('month', r.rn, d.start_date)) as month
,d.start_date
,d.end_date
,datediff('day', greatest(d.start_date, month), least(d.end_date, dateadd('day', -1, dateadd('month', 1, month))))+1 as days_count
FROM (
SELECT
start_date
,end_date
,datediff('month', start_date, end_date) as m
FROM data_table
) as d
JOIN (
SELECT row_number() over (order by null)-1 as rn
FROM table(generator(ROWCOUNT => 1000))
) as r ON r.rn <= d.m
ORDER BY d.start_date, month;