'SQL COUNT number of patients each month

I have a table with:

PATIENT_ID START_DATE END_DATE Ward
1 19/01/2022 19/02/2022 A
2 20/01/2022 19/03/2022 A

And I want to create a summarized table to show for each month, how many patients were active in that ward as well as the total number of patient days for that month. Is this possible in SQL? I'm thinking I might need an external DIM_DATE table that has all of the months up until now and starts from the first START_DATE out of all the PATIENT_ID's but doesn't sound very efficient? Note that I have shown only 1 ward but there are also different wards.

Expected result:

Month Ward COUNT_PATIENTS TOTAL_NUMBER_DAYS
31/01/2022 A 2 33
28/02/2022 A 2 47
31/03/2022 A 1 19


Solution 1:[1]

with data(PATIENT_ID, START_DATE, END_DATE, Ward) as (
    select column1, to_date(column2, 'dd/mm/yyyy'), to_date(column3, 'dd/mm/yyyy'), column4
    from values
        (1, '19/01/2022','19/02/2022','A'),
        (2, '20/01/2022','19/03/2022','A')
), ranges as (
    select 
        date_trunc('month', min(start_date)) as min_start, 
        dateadd('day', -1, dateadd('month', 1, date_trunc('month', max(end_date)))) as max_end
    from data
), gen as (
    select 
        row_number() over(order by null)-1 as rn
    from table(generator(ROWCOUNT => 1000))
), all_months as (
    select 
        dateadd('month', g.rn, date_trunc(month, r.min_start)) as month_start,
        dateadd('day', -1, dateadd('month', 1, month_start)) as month_end
    from ranges as r
    cross join gen as g
    qualify month_start <= r.max_end
)
select 
    a.month_end as month,
    d.ward,
    a.month_end))+1 as days
    count(distinct patient_id) as count_patients,
    sum(datediff(days, greatest(d.start_date, a.month_start), least(d.END_DATE, a.month_end))+1) as total_numbers_days
from all_months as a
left join data as d
    on a.month_start between  date_trunc('month', d.START_DATE) and date_trunc('month', d.END_DATE)
group by 1,2
order by 1,2

gives:

MONTH WARD COUNT_PATIENTS TOTAL_NUMBERS_DAYS
2022-01-31 A 2 25
2022-02-28 A 2 47
2022-03-31 A 1 19

I think your 33 is wrong, as the partials are:

MONTH WARD PATIENT_ID _START _END DAYS
2022-01-31 A 1 2022-01-19 2022-01-31 13
2022-01-31 A 2 2022-01-20 2022-01-31 12
2022-02-28 A 1 2022-02-01 2022-02-19 19
2022-02-28 A 2 2022-02-01 2022-02-28 28
2022-03-31 A 2 2022-03-01 2022-03-19 19

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Simeon Pilgrim