0

I know about Snowflake date function to find out day, week, month, year, etc. I want to have weeks start from Saturday each week to next Saturday. following gives an idea how to extract, but need something to address my specific case. How to get week number of month for any given date on snowflake SQL

If four days or more in week period belong to a certain month, I would assign the week to that month; otherwise, to the next month example: Week of April 29, 2023 to May 5, 2023 has less then four days in April so want to consider it as May Week of May 23, 2023 to June 2nd, 2023 has more than four days in May so I would like to consider it as May

I want to assign weeks to a month with more days of one month (four or more days)

1 Answers1

0

Snowflake will allow you to set the first day of the week with a parameter. https://docs.snowflake.com/en/sql-reference/parameters.html#label-week-start

This will allow you to set the first day of the week at Saturday.

Doing so will result in the WEEK() function counting weeks in a year using saturday as a delimiter between weeks.

Now we just need to find which actual month has the most days for any given week and assign that week to the proper month.

I have an example script below that serves as an example on how to make a custom date dimension table. You can generate the table once and join against it to retrieve your custom date attributes.

    /***************************************************************************
    A WEEK_START session variable of 0 is the default Snowflake behavior 
    and has weeks start on Monday and end of Sunday (ISO standard).
    https://docs.snowflake.com/en/sql-reference/parameters.html#label-week-start
    
    -- 6 = Saturday is day 1 of of the week
    *********************************************************************************************/
    alter session set week_start = 6;
    
    /*********************************************************************************************
    The parameters below define the temporal boundaries of the calendar table. The values must be 
    DATE type and can be hardcoded, the result of a query, or a combination of both.
    For example, you could set date_start and date_end based on the MIN and MAX date of the table
    with the finest date granularity in your data.
    *********************************************************************************************/
    
    SET date_start = TO_DATE('2022-12-18');
    SET date_end = current_date(); --TIP: for the current date use current_date();
    
    --This sets the num_days parameter to the number of days between start and end
    --this value is used for the generator
    set num_days = (select datediff(day, $date_start, $date_end+1));
    
    --CTE to hold generated date range
    create or replace transient table calendar as 
    with gen_cte as (
    select
      dateadd(day,'-' || row_number() over (order by null), 
              dateadd(day, '+1', $date_end)
             ) as date_key
    from table (generator(rowcount => ($num_days)))
    order by 1)

    -- calendar table expressions 
    , step_1 as (
    select
          date_key,
        , dayofmonth(date_key) as day_of_month
        , week(date_key) as week_num --*see comments
        --, dayofweekiso(date_key) as day_of_week_iso,
        , dayofweek(date_key) as day_of_week
        , dayname(date_key) as day_name
        , month(date_key) as month_num
        --, weekiso(date_key) as week_iso_num, --*see comments
        , year(date_key) as year_
        , year_ || '-' ||week_num::string as year_week_key
        , count(date_key) over (partition by year_week_key, month_num) as days_of_week_in_month
    
        --ceil(dayofmonth(date_key) / 7) as day_instance_in_month --used to identify 'floating' events such as "fourth thursday of november" 
    FROM gen_cte)

  -- calculate the max number of days in each month for any week in year
    , step_2 as (
    
    select 
           year_week_key
         , month_num
         , max(step_1.days_of_week_in_month) as max_days_of_week_in_month
    from step_1
        group by year_week_key, month_num)

    -- for any week with 2 actual month values, assign the month with the most number of days
    , step_3 as (

    select 
          year_week_key
        , month_num  
        , row_number() over (partition by year_week_key order by max_days_of_week_in_month desc ) as month_rank
    from step_2
        qualify month_rank = 1
    )
    select
          s1.date_key
        , s1.day_of_month
        , s1.week_num
        , s1.day_of_week
        , s1.day_name
        , s3.month_num as assigned_month_num 
        , s1.month_num as actual_month_num
        , s1.year_
    from step_1 s1
    left join step_3 s3
        on s1.year_week_key = s3.year_week_key
    ;
    
-- select from your new date dimension table
    select * from calendar;

enter image description here

Tom Meacham
  • 161
  • 6