Home > Net >  Snowflake - given a start and end date column, break out each month and count number of days for the
Snowflake - given a start and end date column, break out each month and count number of days for the

Time:03-24

I have the below table (dates_table), that has rows that look like the below, for example with start_date = '2022-01-25' and end_date = '2022-02-04'.

start_date end_date
2022-01-25 2022-02-04
2018-08-31 2018-10-05

and I'm trying to create a new table from the above and add another column days_count, that takes the start_date 1/25, sees how many days are in that month (so 7 in first row because 1/25, 1/26, 1/27, 1/28, 1/29, 1/30, 1/31) and 4 in the next row because 4 days for 2/1 - 2/4:

month start_date end_date days_count
2022-01-01 2022-01-25 2022-02-04 7
2022-02-01 2022-01-25 2022-02-04 4
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

CodePudding user response:

You should add another column to indicate the type of count you're calculating, but you can accomplish this with datediff,last_day, and date_trunc(to get first of month). Here is an example

set start_date='2022-01-25'::date;
set end_date='2022-02-04'::date;

select $start_date,
       $end_date,
       'Start_Remainder' as label,
       datediff(day,$start_date,last_day($start_date)) 1 as days_count
union all
select $start_date,
       $end_date,
       'End_Accrued' as label,
       datediff(day,date_trunc('month',$end_date),$end_date) 1 as days_count

If I were you, I would have the counts in separate columns

select $start_date,
       $end_date,
       datediff(day,$start_date,last_day($start_date)) 1 as Start_Remainder_Days,
       datediff(day,date_trunc('month',$end_date),$end_date) 1 as End_Accrued_Days

CodePudding user response:

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;
  • Related