Snowflake SQL, converting number YYYYWW into date - sql

in snowflake i have numeric column with values like 202201, 202305,202248 that refers to year week combination. How can i convert it into first or last day of tha week?
for example 202251 will be '2022-12-19' or '2022-12-25' (as the week starts on Monday)
thx for help
i have tried
select distinct week_id
,to_date(concat
(
substr(week_id,1,4)
,substr(week_id,5,2)
)
,'YYYYWW'
) as Date_value
from MyTable
but i got only error msg as Can't Parse '202249' as date with format 'YYYYWW'

If we swap to TRY_TO_DATE form, it will not explode, which can make for simpler debugging.
And then when we try 'YYYYWW' we see if fails:
select week_id
,try_to_date(week_id, 'YYYYWW') as Date_value
from values
('202201'),
('202305'),
('202248'),
('202249')
t(week_id);
WEEK_ID
DATE_VALUE
202201
null
202305
null
202248
null
202249
null
swapping to a substring for just the year, and a number for the week:
select week_id
,try_to_date(left(week_id,4), 'YYYY') as just_year
,try_to_number(substr(week_id,5,2)) as week_num
from values
('202201'),
('202305'),
('202248'),
('202249')
t(week_id);
now we get those parts as something workable.
WEEK_ID
JUST_YEAR
WEEK_NUM
202201
2022-01-01
1
202305
2023-01-01
5
202248
2022-01-01
48
202249
2022-01-01
49
Now we can use DATEADD and the WEEK like so:
select week_id
,try_to_date(left(week_id,4), 'YYYY') as just_year
,try_to_number(substr(week_id,5,2)) as week_num
,dateadd(week, week_num, just_year) as answer
from values
('202201'),
('202305'),
('202248'),
('202249')
t(week_id);
WEEK_ID
JUST_YEAR
WEEK_NUM
ANSWER
202201
2022-01-01
1
2022-01-08
202305
2023-01-01
5
2023-02-05
202248
2022-01-01
48
2022-12-03
202249
2022-01-01
49
2022-12-10
which can all be merge into one like:
select week_id
,dateadd(week, try_to_number(substr(week_id,5,2)), try_to_date(left(week_id,4), 'YYYY')) as answer
from values
('202201'),
('202305'),
('202248'),
('202249')
t(week_id);

Related

Is there anyway to check some interval in sql

For example I have a table like this:
CREATE TABLE sales (
id int NOT NULL PRIMARY KEY,
sku text NOT NULL,
date date NOT NULL,
amount real NOT NULL,
CONSTRAINT date_sku UNIQUE (sku,date)
)
Is there anyway to check for each sku if every 2 days average sales is bigger than for example 14 amount sold. I want to find date ranges, the percentage and amount it sold in those days.
dbfiddle
for example for sku B in my example, it sold 15 at 2022-01-01 and 20 at 2022-01-02 and the average is 17.5 for these 2 days which is bigger than 14 therefore it will appear in my result and the change is 17.5 / 14 = 1.25.
Again for the next 2 days we have 20 at 2022-01-02 and 13 at 2022-01-03. Therefore the average is 16.5 which is bigger than 14 and it will appear in the result
but for 13 at 2022-01-03 and 12 at 2022-01-04 and the average is about 12.5. Because 12.5 is not bigger than 14, it will not appear in the result.
my desired output with 14 amount example is:
sku start_date end_date amount_sold change_rate
B 2022-01-01 2022-01-02 17.5 1.25
B 2022-01-02 2022-01-03 16.5 1.17
D 2022-01-01 2022-01-02 28 2
I tried using CASE WHEN but I know that it wont work for large data like one year:
SELECT *
FROM (
SELECT sku,
AVG(CASE WHEN date BETWEEN '2022-01-01' AND '2022-01-02' THEN amount END) AS first_in,
AVG(CASE WHEN date BETWEEN '2022-01-02' AND '2022-01-03' THEN amount END) AS second_in,
AVG(CASE WHEN date BETWEEN '2022-01-03' AND '2022-01-04' THEN amount END) AS third_in
FROM sales
GROUP BY sku
) AS t
WHERE first_in > 14
OR second_in > 14
OR third_in > 14
As a general rule, use the LEAD (or LAG) to retrieve data from the next or previous record. At least this is what I did before you asked for possibly several days. Other window functions are suitable for your need if you want more than 1 day:
SELECT *, averageamount/14
FROM (
SELECT sku, date,
MAX(date) OVER w AS nextdate,
AVG(amount) OVER w AS averageAmount
FROM sales
WINDOW w AS (PARTITION BY sku ORDER BY date RANGE BETWEEN '0 day' PRECEDING AND '2 days' FOLLOWING )
) s
WHERE averageAmount > 14
This above select all the ranges that are up to 3 days long (days D, D+1 and D+2). You may want to remove the ranges that are less than 3 days long by appending the additional condition:
AND nextdate >= date + interval '2 days'

Query a 30 day interval for every 30 day interval in the last year

I want to query every 30 day interval in 2021, but I don't know how to do it without a for loop in SQL.
Here's psuedo code of what I want to do with a table called _table and a date column called application_date:
for _day in range(335):
select '2021-01-01' + _day as start_date, count(*) as _count
from _table
where '2021-01-01' + _day <= application_date <= ('2021-01-01' + _day + interval '30' day )
It would output something like this:
start_date
_count
2021-01-01
{number of rows between 2021-01-01 and 2021-01-31}
2021-01-02
{number of rows between 2021-01-02 and 2021-02-01}
...
...
2021-11-31
{number of rows between 2021-11-31 and 2021-12-30}
2021-12-01
{number of rows between 2021-12-01 and 2021-12-31}
Assuming that you have rows for each day you can group data by date, count it in the group and then use sum window function with range of 30 rows (current + next 30 rows, note that {rows between 2021-01-01 and 2021-01-31} have interval of 31 day, not 30):
-- sample data
WITH dataset(start_date) AS (
VALUES (date '2021-01-01'),
(date '2021-01-01'),
(date '2021-01-01'),
(date '2021-01-02'),
(date '2021-01-03'),
(date '2021-01-03')
)
-- query
select start_date
, sum(cnt) over (order by start_date ROWS BETWEEN CURRENT ROW AND 30 FOLLOWING) rolling_count_31_days
from (
select start_date
, count(*) cnt
from dataset
where year(start_date) = 2021
group by start_date
)
Output:
start_date
rolling_count_31_days
2021-01-01
6
2021-01-02
3
2021-01-03
2
If some dates are missing - checkout this or this answer describing how to insert missing dates and insert dates into the group result with cnt set to 0.
Note that Trino (the new name for PrestoSQL) updated support for RANGE frame type and you can implement this without need to insert missing rows.

How can I create a week group column in my Date Dimension Table based on this specific logic?

I am using SQL Server 2014 and I have a table in my Database called Date Dimension.
It has a column called "Date" which contains daily dates from 01 January 2013 to 31 December 2025 (extract of the column given below):
Date
2022-01-01
2022-01-02
2022-01-03
2022-01-04
2022-01-05
2022-01-06
2022-01-07
2022-01-09
...
2022-01-30
2022-01-31
...
I need to create a new column in the Date Dimension Table called "HR_WeekGroup" based on the following logic:
A WeekGroup will start on a Monday and end on a Sunday. WeekGroups will be calculated for EACH Month separately. That is, if the 1st Day of a Month is a Saturday, the WeekGroup for that week will consist of only 2 days (Saturday and Sunday) and if the last Day of that Month is a Monday, the weekGroup will consist of only 1 Day (Monday).
Here is what I'm after (based on the Month of January 2022):
Date HR_WeekGroup
2022-01-01 Wk 01Jan2022-02Jan2022
2022-01-02 Wk 01Jan2022-02Jan2022
2022-01-03 Wk 03Jan2022-09Jan2022
2022-01-04 Wk 03Jan2022-09Jan2022
2022-01-05 Wk 03Jan2022-09Jan2022
2022-01-06 Wk 03Jan2022-09Jan2022
2022-01-07 Wk 03Jan2022-09Jan2022
2022-01-09 Wk 03Jan2022-09Jan2022
... ...
2022-01-30 Wk 24Jan2022-30Jan2022
2022-01-31 Wk 31Jan2022
...
What would be the T-SQL Code that would allow me to create this column?
I had a look at the following pages but I can't figure out how to apply the examples to my specific problem.
What is the SQL syntax to create a column in my Date Dimension Table that will group the dates into this specific week grouping?
How to Group Data by Week in SQL Server
Set DateFirst 1;
-- The above changes the start of the week to Monday.
-- This makes the week as starting on Monday and ending on Sunday
-- DayOfWeek 1 means Monday
-- DayOfWeek 7 means Sunday
with cte as (
select
Date_Column,
MONTH(Date_Column) as [MONTH], -- Month in the year
DAY(Date_Column) as [DAY], -- Day in the month
DATEPART(week,Date_Column) as [WeekOfTheYear], --Week number of the year
DATEPART(dw,Date_Column) as [DayOfWeek], -- The week day of the week
Min(Date_Column) OVER (Partition by DATEPART(week,Date_Column), MONTH(Date_Column), YEAR(Date_Column) ) as [MinDateWeekGroup],
Max(Date_Column) OVER (Partition by DATEPART(week,Date_Column), MONTH(Date_Column), YEAR(Date_Column) ) as [MaxDateWeekGroup]
from date_dimension
--order by YEAR(Date_Column), MONTH(Date_Column), DAY(Date_Column)
)
select
Date_Column,
case when [MinDateWeekGroup] = [MaxDateWeekGroup]
THEN CONCAT('Wk ',
Right('0'+Convert(varchar(10), Day([MinDateWeekGroup])),2),
Convert(char(3), [MinDateWeekGroup], 0),Convert(char(4),YEAR([MinDateWeekGroup])) )
ELSE
CONCAT('Wk ',
Right('0'+Convert(varchar(10), Day([MinDateWeekGroup])),2),
Convert(char(3), [MinDateWeekGroup], 0),Convert(char(4),YEAR([MinDateWeekGroup])),
'-',
Right('0'+Convert(varchar(10), Day([MaxDateWeekGroup])),2),
Convert(char(3), [MaxDateWeekGroup], 0),Convert(char(4),YEAR([MaxDateWeekGroup]))
)
END as [HR_WeekGroup]
from cte
order by YEAR(Date_Column), MONTH(Date_Column), DAY(Date_Column)

(SQL BigQuery) Using Lag but data contains missing months

I have the following table with monthly data. But we do not have the third month.
DATE
FREQUENCY
2021-01-01
6000
2021-02-01
4533
2021-04-01
7742
2021-05-01
1547
2021-06-01
9857
I want to get the frequency of the previous month into the following table.
DATE
FREQUENCY
PREVIOUS_MONTH_FREQ
2021-01-01
6000
NULL
2021-02-01
4533
6000
2021-04-01
7742
NULL
2021-05-01
1547
7742
2021-06-01
9857
1547
I want the 2021-04-01 record to have NULL for the PREVIOUS_MONTH_FREQ since there is no data for the previous month.
I got so far as...
SELECT DATE,
FREQUENCY,
LAG(FREQUENCY) OVER(ORDER BY DATE) AS PREVIOUS_MONTH_FREQ
FROM Table1
Use a CASE expression to check if the previous row contains data of the previous month:
SELECT DATE,
FREQUENCY,
CASE WHEN DATE_SUB(DATE, INTERVAL 1 MONTH) = LAG(DATE) OVER(ORDER BY DATE)
THEN LAG(FREQUENCY) OVER(ORDER BY DATE)
END AS PREVIOUS_MONTH_FREQ
FROM Table1
See the demo.
In BigQuery, you can use a RANGE window specification. This only trick is that you need a number rather than a date:
select t.*,
max(frequency) over (order by date_diff(date, date '2000-01-01', month)
range between 1 preceding and 1 preceding
) as prev_frequence
from t;
The '2000-01-01' is an arbitrary date. This turns the date column into the number of months since that date. The actual date is not important.

Split single row into multiple rows based on week of the month in date

I have a table like this
Id Valid_From Valid_To
9744 24/06/2019 07/07/2019
9745 12/08/2019 31/12/9999
I would like to split this table into multiple rows based on the week like this by joining to the date table
Id Valid_from Valid_To Month Week
9744 24/06/2019 07/07/2019 June 4
9744 24/06/2019 07/07/2019 July 1
9744 24/06/2019 07/07/2019 July 2
9745 12/08/2019 31/12/9999 August 2
9745 12/08/2019 31/12/9999 August 3
9745 12/08/2019 31/12/9999 August 4
In this case there will be 3 rows as the valid from and valid two falls between these 3 weeks for ID - 9744
For ID - 9745 the Valid_to date is infinity so we need to just take all the week in the current month from the valid_from date
I then just need to append the output with Month and the Week number
Can someone help me to write a query to have this output?
Thanks
You mention a "date" table. If you have one then you can use a join like this:
select distinct t.id, valid_from, t.valid_to, d.month, d.week
from yourtable t join
date d
on d.date >= t.valid_from and
d.date <= t.valid_to;
If I understand your question right, you need to list all month names and week numbers of these months' existing between valid_from and valid_to dates. I did it by following query:
SELECT
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER
FROM
(
SELECT
CEIL((Q.DATES_BETWEEN_INTERVAL - FIRST_DAY_OF_MONTH + 1) / 7) WEEK_NUMBER,
TO_CHAR(Q.DATES_BETWEEN_INTERVAL, 'MONTH', 'NLS_DATE_LANGUAGE = American') MONTH_NAME,
Q.*
FROM
(
SELECT
LEVEL + S.VALID_FROM DATES_BETWEEN_INTERVAL,
TRUNC(LEVEL + S.VALID_FROM, 'MONTH') FIRST_DAY_OF_MONTH,
S.* FROM
(
SELECT T.*,
(CASE WHEN EXTRACT(YEAR FROM T.VALID_TO) = 9999 THEN LAST_DAY(T.VALID_FROM) ELSE T.VALID_TO END) - T.VALID_FROM DAYS_COUNT
FROM AAA_TABLE T
) S
CONNECT BY LEVEL <= S.DAYS_COUNT
) Q
) Q
GROUP BY
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER
ORDER BY
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER;
But there must be 5th week if the date greater than 28th day of month. Hope this will help you.