Related
I want to create a flag in Bigquery which will return 1 when true and 0 when false. The statement works fine when it has to return the "else" value which is 0. However, when it satisfies the condition, it returns two rows with both 1 and 0 in them. Why is this happening?
Below is the code used:
table AS(
SELECT
id,
month,
ROUND((text/(month_days/7)), 2) AS value
FROM (SELECT id, extract(month FROM date) AS month,
(32 - EXTRACT(DAY FROM DATE_ADD(DATE_TRUNC(DATE(date), MONTH), INTERVAL 31 DAY))) AS month_days,
sum(text_sent) AS text
FROM table1
WHERE date BETWEEN '2020-01-01 00:00:00 UTC' AND '2020-06-30 00:00:00 UTC'
GROUP BY 1,2,3)),
table_flag AS(
SELECT
id,
CASE
WHEN month = 1 AND value > 100 THEN 1
WHEN month = 2 AND value > 150 THEN 1
WHEN month = 3 AND value > 130 THEN 1
WHEN month = 4 AND value > 200 THEN 1
WHEN month = 5 AND value > 235 THEN 1
WHEN month = 6 AND value > 125 THEN 1
WHEN month = 7 AND value > 324 THEN 1
WHEN month = 8 AND value > 160 THEN 1
WHEN month = 9 AND value > 350 THEN 1
WHEN month = 10 AND value > 80 THEN 1
WHEN month = 11 AND value > 245 THEN 1
ELSE 0
END AS value_flag
FROM
table)
SELECT
t.id,
t.value,
t.month,
tf.value_flag
FROM
table t
LEFT JOIN
table_flag tf
ON
t.id = tf.id
WHERE t.id IS NOT NULL
GROUP BY 1,2,3,4
ORDER BY 1
I have also tried nested IF, but that doesn't work either:
SELECT DISTINCT(id),
(IF((month = 1 AND value > 100), 1,
(IF((month = 2 AND value > 150), 1,
(IF((month = 3 AND value > 130), 1,
(IF((month = 4 AND value > 200), 1,
(IF((month = 5 AND value > 235), 1,
(IF((month = 6 AND value > 125), 1,
(IF((month = 7 AND value > 324), 1,
(IF((month = 8 AND value > 160), 1,
(IF((month = 9 AND value > 350), 1,
(IF((month = 10 AND value > 80), 1,
(IF((month = 11 AND value > 245), 1,0))))))))))))))))))))))
AS value_flag
FROM table)
This is how the output looks right now (This is NOT what I want):
enter image description here
The output is completely wrong. Please suggest alternate method (if any) to do it.
P.S.: This is my first question here, please let me know if any other information is needed. Thanks in advance for the help!
Both table and table_flag has several rows with identical id. BigQuery for each row in table finds several rows in table_flag. To remove duplicates we could add month to table_flag and to the ON clause. But we actually do not need the last LEFT JOIN. Try this:
WITH table AS(
SELECT
id,
month,
ROUND((text/(month_days/7)), 2) AS value
FROM (
SELECT
id,
extract(month FROM date) AS month,
(32 - EXTRACT(DAY FROM DATE_ADD(DATE_TRUNC(DATE(date), MONTH), INTERVAL 31 DAY))) AS month_days,
sum(text_sent) AS text
FROM table1
WHERE
date BETWEEN '2020-01-01 00:00:00 UTC' AND '2020-06-30 00:00:00 UTC'
AND id IS NOT NULL
GROUP BY 1,2,3
)
)
SELECT
id,
value,
month,
CASE
WHEN month = 1 AND value > 100 THEN 1
WHEN month = 2 AND value > 150 THEN 1
WHEN month = 3 AND value > 130 THEN 1
WHEN month = 4 AND value > 200 THEN 1
WHEN month = 5 AND value > 235 THEN 1
WHEN month = 6 AND value > 125 THEN 1
WHEN month = 7 AND value > 324 THEN 1
WHEN month = 8 AND value > 160 THEN 1
WHEN month = 9 AND value > 350 THEN 1
WHEN month = 10 AND value > 80 THEN 1
WHEN month = 11 AND value > 245 THEN 1
ELSE 0
END AS value_flag
FROM table
ORDER BY 1
or this:
SELECT
id,
month,
ROUND((text/(month_days/7)), 2) AS value,
CASE
WHEN month = 1 AND ROUND((text/(month_days/7)), 2) > 100 THEN 1
WHEN month = 2 AND ROUND((text/(month_days/7)), 2) > 150 THEN 1
WHEN month = 3 AND ROUND((text/(month_days/7)), 2) > 130 THEN 1
WHEN month = 4 AND ROUND((text/(month_days/7)), 2) > 200 THEN 1
WHEN month = 5 AND ROUND((text/(month_days/7)), 2) > 235 THEN 1
WHEN month = 6 AND ROUND((text/(month_days/7)), 2) > 125 THEN 1
WHEN month = 7 AND ROUND((text/(month_days/7)), 2) > 324 THEN 1
WHEN month = 8 AND ROUND((text/(month_days/7)), 2) > 160 THEN 1
WHEN month = 9 AND ROUND((text/(month_days/7)), 2) > 350 THEN 1
WHEN month = 10 AND ROUND((text/(month_days/7)), 2) > 80 THEN 1
WHEN month = 11 AND ROUND((text/(month_days/7)), 2) > 245 THEN 1
ELSE 0
END AS value_flag
FROM (
SELECT
id,
extract(month FROM date) AS month,
(32 - EXTRACT(DAY FROM DATE_ADD(DATE_TRUNC(DATE(date), MONTH), INTERVAL 31 DAY))) AS month_days,
sum(text_sent) AS text
FROM table1
WHERE
date BETWEEN '2020-01-01 00:00:00 UTC' AND '2020-06-30 00:00:00 UTC'
AND id IS NOT NULL
GROUP BY 1,2,3
)
ORDER BY 1
Further Evolved : The code below satisfies what was wanted in earlier comments [if you follow history] :), my new question right below is stumping me.
Question: I want to have 'Week_Of_Year' Adjust every Sunday, the code below does it every Monday.. I have tried 1 and 0 but via alter session per Snowflake but no luck! Any idea how to have a new week triggered by Sunday not Monday (as that is what it is currently)?
ie the result I want is for Date:1/2/2005 to reflect Week_Of_Year as 2 , not 1.
alter session set week_of_year_policy = 1;
vs
alter session set week_of_year_policy = 0;
WORKING CODE
create or replace temporary table test_temptable
(
DATE_ID SMALLINT NOT NULL
,FULL_DATE DATE NOT NULL
,DATE Varchar(10) NOT NULL
,YEAR SMALLINT NOT NULL
,WEEK_OF_YEAR SMALLINT NOT NULL
,DAY_OF_YEAR SMALLINT NOT NULL
,QTR_NUMBER SMALLINT NOT NULL
,DAY_OF_QUARTER SMALLINT NOT NULL
,MONTH_OF_YEAR SMALLINT NOT NULL
,MONTH_NAME CHAR(3) NOT NULL --need to have full month name, if it comes to it maybe do if logic
,DAY_OF_MONTH SMALLINT NOT NULL
,DAY_OF_WEEK VARCHAR(9) NOT NULL
,DAY_NAME VARCHAR(12) NOT NULL
,DAY_IS_WEEKDAY boolean NOT Null
,DAY_IS_LAST_OF_MONTH boolean NOT Null
,DAY_OF_WEEK_IN_MONTH SMALLINT NOT NULL
,HOLIDAYUSA VARCHAR(80) ----left out NOT NULL on Purpose
--- ,DAY_IS_HOLIDAY boolean NOT NULL
)
AS
WITH MY_DATES AS (
SELECT DATEADD(DAY, SEQ4(), '2005-01-01') AS Full_DATE
,(seq8()+ 1) AS date_id
,DATE_TRUNC('QUARTER',Full_DATE) as Q
,DATEDIFF('day',Q, Full_DATE) as Day_of_Quarter
/*logic to support Easter Day calculation */
,Full_DATE as SinCurDay
,MONTH(Full_Date) as inCurMonth
,YEAR(Full_Date) as inCurYear
,FLOOR(inCurYear/100) as inCurCent
,inCurYear%19 as inYear
,FLOOR((inCurCent-17)/25) as inYearTmp
,(inCurCent-FLOOR(inCurCent/4)-FLOOR((inCurCent-inYearTmp)/3)+(19*inYear)+15)%30 as inTemp2a
,inTemp2a-FLOOR(inTemp2a/28)*(1 - FLOOR(inTemp2a/28)*FLOOR(29/(inTemp2a+1))*FLOOR((21-inYear)/11)) as inTemp2b
,(inCurYear+FLOOR(inCurYear/4)+inTemp2b+2-inCurCent+FLOOR(inCurCent/4))%7 as inTemp3
,inTemp2b-inTemp3 as inTemp4
,3+FLOOR((inTemp4+40)/44) as inEastMontha
,inTemp4+28-31*FLOOR(inEastMontha/4) as inEastDay
,inEastMontha /*- 1*/ as inEastMonthb
,Date_from_parts(inCurYear,inEastMonthb, inEastDay) as EasterDay
/*End Easter Day Logic */
/*Day of Week in Month*/
/* CASE
WHEN Day(Full_Date) < 8 THEN 1
WHEN Day(Full_Date) BETWEEN 7 AND 14 then 2
WHEN Day(Full_Date) BETWEEN 14 AND 21 then 3
WHEN Day(Full_Date) BETWEEN 21 AND 28 then 4
ELSE 5 End as Day_In_Month
*/
FROM TABLE(GENERATOR(ROWCOUNT=>365))
)
SELECT date_id
,Full_Date
,to_varchar(Full_Date, 'mm/dd/yyyy')
,YEAR(Full_Date)
,WEEKOFYEAR(Full_Date)
,DAYOFYEAR(Full_Date)
,QUARTER(Full_Date)
,Day_Of_Quarter + 1
,MONTH(Full_Date)
,MONTHNAME(Full_Date)
,DAY(Full_Date)
,DAYOFWEEK(Full_Date) + 1
,DAYNAME(Full_Date)
/*Weekend boolean */
,CASE
WHEN DAYOFWEEK(Full_date) + 1 = 7 THEN FALSE
WHEN DAYOFWEEK(Full_date) + 1 = 1 THEN FALSE
ELSE TRUE END
/*Last Day Of Month Boolean*/
,CASE
WHEN Full_Date = last_day(Full_Date) THEN True
ELSE FALSE END
/*Week in Month*/
---,CAST(Round((day(Full_Date) +6)/7,0) as VARCHAR)
,CASE
WHEN Day(Full_Date) < 8 THEN 1
WHEN Day(Full_Date) BETWEEN 7 AND 14 then 2
WHEN Day(Full_Date) BETWEEN 14 AND 21 then 3
WHEN Day(Full_Date) BETWEEN 21 AND 28 then 4
ELSE 5 End
/*HolidayUSA Logic */
,CASE
WHEN MONTH(Full_Date) = 10 AND DAY(Full_Date) = 31 THEN 'Halloween'
/*ThanksGiving*/
WHEN MONTH(Full_Date) = 11 AND DAYOFWEEK(Full_Date) + 1 = 5 AND
CASE
WHEN Day(Full_Date) < 8 THEN 1
WHEN Day(Full_Date) BETWEEN 7 AND 14 then 2
WHEN Day(Full_Date) BETWEEN 14 AND 21 then 3
WHEN Day(Full_Date) BETWEEN 21 AND 28 then 4
ELSE 5 End = 4
THEN 'Thanksgiving Day' -- should I add ()
WHEN MONTH(Full_Date) = 11 AND DAYOFWEEK(Full_Date) + 1 = 6 AND
CASE
WHEN Day(Full_Date) < 8 THEN 1
WHEN Day(Full_Date) BETWEEN 7 AND 14 then 2
WHEN Day(Full_Date) BETWEEN 14 AND 21 then 3
WHEN Day(Full_Date) BETWEEN 21 AND 28 then 4
ELSE 5 End = 4
THEN 'Black Friday' -- should I add ()
WHEN MONTH(Full_Date) = 12 AND DAY(Full_Date) = 25 THEN 'Christmas Day'
WHEN MONTH(Full_Date) = 7 AND DAY(Full_Date) = 4 THEN 'Independence Day'
WHEN MONTH(Full_Date) = 12 AND DAY(Full_Date) = 31 THEN 'New Years Eve'
WHEN MONTH(Full_Date) = 1 AND DAY(Full_Date) = 1 THEN 'New Years Day'
WHEN MONTH(Full_Date) = 5 AND DAYOFWEEK(Full_Date)+ 1 = 2 AND Day(Full_Date) > '24' then 'Memorial Day'
WHEN MONTH(Full_Date) = 9 AND DAYOFWEEK(Full_Date) + 1 = 2 AND Day(Full_Date) < '8'THEN 'Labor Day'
/*Martin Luther King Jr Day */
WHEN MONTH(Full_Date) = 1 AND DAYOFWEEK(Full_Date) + 1 = 2 AND
CASE
WHEN Day(Full_Date) < 8 THEN 1
WHEN Day(Full_Date) BETWEEN 7 AND 14 then 2
WHEN Day(Full_Date) BETWEEN 14 AND 21 then 3
WHEN Day(Full_Date) BETWEEN 21 AND 28 then 4
ELSE 5 End
= 3 THEN 'Martin Luther King Jr Day'
/*Presidents Day*/
WHEN MONTH(Full_Date) = 2 AND DAYOFWEEK(Full_Date) + 1 = 2 AND
CASE
WHEN Day(Full_Date) < 8 THEN 1
WHEN Day(Full_Date) BETWEEN 7 AND 14 then 2
WHEN Day(Full_Date) BETWEEN 14 AND 21 then 3
WHEN Day(Full_Date) BETWEEN 21 AND 28 then 4
ELSE 5 END
= 3 THEN 'Presidents Day'
WHEN MONTH(Full_Date) = 11 AND DAY(Full_Date) = 11 THEN 'Veterans Day'
/*Mothers Day */
WHEN MONTH(Full_Date) = 5 AND DAYOFWEEK(Full_Date) + 1 = 1 AND
CASE
WHEN Day(Full_Date) < 8 THEN 1
WHEN Day(Full_Date) BETWEEN 7 AND 14 then 2
WHEN Day(Full_Date) BETWEEN 14 AND 21 then 3
WHEN Day(Full_Date) BETWEEN 21 AND 28 then 4
ELSE 5 END
= 2 THEN 'Mothers Day'
/*Fathers Day */
WHEN MONTH(Full_Date) = 6 AND DAYOFWEEK(Full_Date) + 1 = 1 AND
CASE
WHEN Day(Full_Date) < 8 THEN 1
WHEN Day(Full_Date) BETWEEN 7 AND 14 then 2
WHEN Day(Full_Date) BETWEEN 14 AND 21 then 3
WHEN Day(Full_Date) BETWEEN 21 AND 28 then 4
ELSE 5 END
= 3 THEN 'Fathers Day'
WHEN MONTH(Full_Date) = 2 AND DAY(Full_Date) = 14 THEN 'Valentines Day'
WHEN Full_Date = EasterDay THEN 'Easter Day'
WHEN Full_Date = EasterDay - 2 THEN 'Good Friday'
ELSE NULL END
--- ,CASE
--- WHEN HOLIDAYUSA is not NULL THEN TRUE Else False
--- END
FROM MY_DATES
Order By Full_Date;
Evolved Question: Please view updated code, where I translated the T-SQL logic to Calculate Easter into something Snowflake could understand. The only issue is that for Year 2005 I am off by one day ( the code below returns March 28, 2005 for Easter but it is March 27, 2008).
Question: Can someone help me understand why the below line is so close but wrong, I have tried so many variations of rtrim, RIGHT, and other manipulations to the values that feed the date thinking it had to do with rounding down, but all making the return easter dates further off. Below is my closest for a 20 year span , all within about 1-5 days of actual.
Line that is causing error:date_from_parts(YR, '0' + rtrim(EasterMonth),'0' + rtrim(EasterDay)) AS test6
Easter Logic
(24 + 19 * (YR % 19)) % 30 AS EpactCalc,
EpactCalc - (EpactCalc / 28) AS PaschalDaysCalc,
PaschalDaysCalc - ((YR + YR / 4 + PaschalDaysCalc - 13) % 7) AS NumOfDaysToSunday,
3 + (NumOfDaysToSunday + 40) / 44 AS EasterMonth,
NumOfDaysToSunday + 28 - (31 * (EasterMonth / 4)) AS EasterDay, ---EasterMonth + RTRIM(YR) as test6
---to_date_from_parts(YR,(("0" + EasterMonth).substr(-2)), (("0" + EasterDay).substr(-2)) as test6
date_from_parts(YR, '0' + rtrim(EasterMonth),'0' + rtrim(EasterDay)) AS test6
Full Script:
CREATE OR REPLACE
TEMPORARY TABLE .test_temptable (Date_Id SMALLINT NOT NULL ,Full_Date DATE NOT NULL ,Date Varchar(10) NOT NULL ,YEAR SMALLINT NOT NULL ,WEEK_OF_YEAR SMALLINT NOT NULL ,DAY_OF_YEAR SMALLINT NOT NULL ,QTR_Number SMALLINT NOT NULL ,Day_Of_Quarter SMALLINT NOT NULL,MONTH_OF_YEAR SMALLINT NOT NULL ,MONTH_NAME CHAR(3) NOT NULL --need to have full month name, if it comes to it maybe do if logic
,DAY_OF_MONTH SMALLINT NOT NULL ,DAY_OF_WEEK VARCHAR(9) NOT NULL ,DAY_NAME VARCHAR(12) NOT NULL ,DAY_IS_WEEKDAY boolean NOT NULL,DAY_IS_LAST_OF_MONTH boolean NOT NULL ,DAY_OF_WEEK_IN_MONTH SMALLINT NOT NULL ,HOLIDAYUSA VARCHAR(80) ----left out NOT NULL on Purpose
,test1 smallint NOT NULL,test2 smallint NOT NULL ,test3 smallint NOT NULL ,test4 smallint NOT NULL,test5 smallint NOT NULL ,test6 DATE NOT NULL) AS WITH CTE_MY_DATE AS
(---Returns a sequence of monotonically increasing integers, with wrap-around. Wrap-around occurs after the largest representable integer of the integer width (1, 2, 4, or 8 byte)..??I'd like to understand this a tad bit better.Is SEQ4 for float?
SELECT DATEADD(DAY, SEQ4(), '2005-01-01') AS Full_DATE,
YEAR(Full_Date) AS YR,
(seq8()+ 1) AS date_id,
DATE_TRUNC('QUARTER',Full_DATE) AS q,
DATEDIFF('day',q, Full_DATE) AS Day_of_Quarter,
(24 + 19 * (YR % 19)) % 30 AS EpactCalc,
EpactCalc - (EpactCalc / 28) AS PaschalDaysCalc,
PaschalDaysCalc - ((YR + YR / 4 + PaschalDaysCalc - 13) % 7) AS NumOfDaysToSunday,
3 + (NumOfDaysToSunday + 40) / 44 AS EasterMonth,
NumOfDaysToSunday + 28 - (31 * (EasterMonth / 4)) AS EasterDay, ---EasterMonth + RTRIM(YR) as test6
---to_date_from_parts(YR,(("0" + EasterMonth).substr(-2)), (("0" + EasterDay).substr(-2)) as test6
date_from_parts(YR, '0' + rtrim(EasterMonth),'0' + rtrim(EasterDay)) AS test6
FROM TABLE(GENERATOR(ROWCOUNT=>9125))
)
SELECT date_id ,
Full_Date ,
to_varchar(Full_Date, 'mm/dd/yyyy') ,
YEAR(Full_Date) ,
WEEKOFYEAR(Full_Date) ,
DAYOFYEAR(Full_Date) ,
QUARTER(Full_Date) ,
Day_Of_Quarter + 1 ,
MONTH(Full_Date) ,
MONTHNAME(Full_Date) ,
DAY(Full_Date) ,
DAYOFWEEK(Full_Date) + 1 ,
DAYNAME(Full_Date) ---calculates if it is on weekend or not
,
CASE
WHEN DAYOFWEEK(Full_date) = 7 THEN FALSE
WHEN DAYOFWEEK(Full_date) = 1 THEN FALSE
ELSE TRUE
END ----calculates if last day of month
,
CASE
WHEN Full_Date = last_day(Full_Date) THEN TRUE
ELSE FALSE
END,
CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) -- this is what week you are in in the month, double check that what it ought to be
--- calculates holidays, is Thxgiving always in the fifth week?,
,
CASE
WHEN MONTH(Full_Date) = 10
AND DAY(Full_Date) = 31 THEN 'Halloween'
WHEN MONTH(Full_Date) = 11
AND DAYOFWEEK(Full_Date) + 1 = 4
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 5 THEN 'Thanksgiving Day'
WHEN MONTH(Full_Date) = 12
AND DAY(Full_Date) = 25 THEN 'Christmas Day'
WHEN MONTH(Full_Date) = 7
AND DAY(Full_Date) = 4 THEN 'Independence Day' --adding
WHEN MONTH(Full_Date) = 12
AND DAY(Full_Date) = 31 THEN 'New Years Eve'
WHEN MONTH(Full_Date) = 1
AND DAY(Full_Date) = 1 THEN 'New Years Day' ---memorial day attempt
WHEN MONTH(Full_Date) = 5
AND DAYOFWEEK(Full_Date)+ 1 = 2
AND Day(Full_Date) > '24' THEN 'Memorial Day' ---labor day
WHEN MONTH(Full_Date) = 9
AND DAYOFWEEK(Full_Date) + 1 = 2
AND Day(Full_Date) < '8'THEN 'Labor Day'
WHEN MONTH(Full_Date) = 1
AND DAYOFWEEK(Full_Date) = 2
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 3 THEN 'Martin Luther King Jr Day'
WHEN MONTH(Full_Date) = 2
AND DAYOFWEEK(Full_Date) = 2
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 3 THEN 'Presidents Day'
WHEN MONTH(Full_Date) = 11
AND DAY(Full_Date) = 11 THEN 'Veterans Day' ---added Mother's Day
WHEN MONTH(Full_Date) = 5
AND DAYOFWEEK(Full_Date) + 1 = 1
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 2 THEN 'Mothers Day'
WHEN MONTH(Full_Date) = 6
AND DAYOFWEEK(Full_Date) + 1 = 1
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 3 THEN 'Fathers Day'
WHEN MONTH(Full_Date) = 2
AND DAY(Full_Date) = 14 THEN 'Valentines Day' ---easter
---good friday
ELSE NULL
END ,
EpactCalc ,
PaschalDaysCalc ,
NumOfDaysToSunday ,
EasterMonth ,
EasterDay ,
test6
FROM CTE_MY_DATE;
Old Question:
I have this nice piece of code for snowflake users that I need a little help finishing. I specifically want to use the second chunk of code that was written for SqlServer to be used in Snowflakes env, and integrated into my script below (first chunk of code).
Specifically:
a)"How do you integrate a function into a query like this" as in Advice where to put the code inside my script above because I am having trouble understanding how to integrate a function within a select statement
b)"Is there anything glaring about this query that would make running it in Snowflake uniquely difficult" I attempted to run the SQLServer "easter date" code alone inside snowflake, and changed the variables to match snowflakes requirements (ie take out #) and then I got an error unexpected 'BEGIN'.
CREATE OR REPLACE
TEMPORARY TABLE test_temptable (Date_Id SMALLINT NOT NULL ,Full_Date DATE NOT NULL ,Date Varchar(10) NOT NULL ,YEAR SMALLINT NOT NULL ,WEEK_OF_YEAR SMALLINT NOT NULL ,DAY_OF_YEAR SMALLINT NOT NULL ,QTR_Number SMALLINT NOT NULL ,Day_Of_Quarter SMALLINT NOT NULL,MONTH_OF_YEAR SMALLINT NOT NULL ,MONTH_NAME CHAR(3) NOT NULL
,DAY_OF_MONTH SMALLINT NOT NULL ,DAY_OF_WEEK VARCHAR(9) NOT NULL ,DAY_NAME VARCHAR(12) NOT NULL ,DAY_IS_WEEKDAY boolean NOT NULL,DAY_IS_LAST_OF_MONTH boolean NOT NULL ,DAY_OF_WEEK_IN_MONTH SMALLINT NOT NULL ,HOLIDAYUSA VARCHAR(80)
(
SELECT DATEADD(DAY, SEQ4(), '2005-01-01') AS Full_DATE,
(seq8()+ 1) AS date_id,
DATE_TRUNC('QUARTER',Full_DATE) AS q,
DATEDIFF('day',q, Full_DATE) AS Day_of_Quarter
FROM TABLE(GENERATOR(ROWCOUNT=>366))
)
SELECT date_id ,
Full_Date ,
to_varchar(Full_Date, 'mm/dd/yyyy') ,
YEAR(Full_Date) ,
WEEKOFYEAR(Full_Date) ,
DAYOFYEAR(Full_Date) ,
QUARTER(Full_Date) ,
Day_Of_Quarter + 1 ,
MONTH(Full_Date) ,
MONTHNAME(Full_Date) ,
DAY(Full_Date) ,
DAYOFWEEK(Full_Date) + 1 ,
DAYNAME(Full_Date)
,
CASE
WHEN DAYOFWEEK(Full_date) = 7 THEN FALSE
WHEN DAYOFWEEK(Full_date) = 1 THEN FALSE
ELSE TRUE
END
,
CASE
WHEN Full_Date = last_day(Full_Date) THEN TRUE
ELSE FALSE
END,
CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR)
,
CASE
WHEN MONTH(Full_Date) = 10
AND DAY(Full_Date) = 31 THEN 'Halloween'
WHEN MONTH(Full_Date) = 11
AND DAYOFWEEK(Full_Date) + 1 = 4
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 5 THEN 'Thanksgiving Day'
WHEN MONTH(Full_Date) = 12
AND DAY(Full_Date) = 25 THEN 'Christmas Day'
WHEN MONTH(Full_Date) = 7
AND DAY(Full_Date) = 4 THEN 'Independence Day' --adding
WHEN MONTH(Full_Date) = 12
AND DAY(Full_Date) = 31 THEN 'New Years Eve'
WHEN MONTH(Full_Date) = 1
AND DAY(Full_Date) = 1 THEN 'New Years Day' ---memorial day attempt
WHEN MONTH(Full_Date) = 5
AND DAYOFWEEK(Full_Date)+ 1 = 2
AND Day(Full_Date) > '24' THEN 'Memorial Day' ---labor day
WHEN MONTH(Full_Date) = 9
AND DAYOFWEEK(Full_Date) + 1 = 2
AND Day(Full_Date) < '8'THEN 'Labor Day'
WHEN MONTH(Full_Date) = 1
AND DAYOFWEEK(Full_Date) = 2
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 3 THEN 'Martin Luther King Jr Day'
WHEN MONTH(Full_Date) = 2
AND DAYOFWEEK(Full_Date) = 2
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 3 THEN 'Presidents Day'
WHEN MONTH(Full_Date) = 11
AND DAY(Full_Date) = 11 THEN 'Veterans Day' ---added Mother's Day
WHEN MONTH(Full_Date) = 5
AND DAYOFWEEK(Full_Date) + 1 = 1
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 2 THEN 'Mothers Day'
WHEN MONTH(Full_Date) = 6
AND DAYOFWEEK(Full_Date) + 1 = 1
AND CAST(Round((day(Full_Date) +6)/7,0) AS VARCHAR) = 3 THEN 'Fathers Day'
WHEN MONTH(Full_Date) = 2
AND DAY(Full_Date) = 14 THEN 'Valentines Day' ---easter
---good friday
ELSE NULL
END
FROM CTE_MY_DATE;
Below is the SQLServer code I need help to input the above!! (thanks Function to return date of Easter for the given year)
CREATE FUNCTION dbo.GetEasterSunday
( #Y INT )
RETURNS SMALLDATETIME
AS
BEGIN
DECLARE #EpactCalc INT,
#PaschalDaysCalc INT,
#NumOfDaysToSunday INT,
#EasterMonth INT,
#EasterDay INT
SET #EpactCalc = (24 + 19 * (#Y % 19)) % 30
SET #PaschalDaysCalc = #EpactCalc - (#EpactCalc / 28)
SET #NumOfDaysToSunday = #PaschalDaysCalc - (
(#Y + #Y / 4 + #PaschalDaysCalc - 13) % 7
)
SET #EasterMonth = 3 + (#NumOfDaysToSunday + 40) / 44
SET #EasterDay = #NumOfDaysToSunday + 28 - (
31 * (#EasterMonth / 4)
)
RETURN
(
SELECT CONVERT
( SMALLDATETIME,
RTRIM(#Y)
+ RIGHT('0'+RTRIM(#EasterMonth), 2)
+ RIGHT('0'+RTRIM(#EasterDay), 2)
)
)
END
GO
New answer:
You must use month and day numerical values directly, not reformat as TEXT:
DATE_FROM_PARTS(Year, EasterMonth, EasterDay)
Old answer:
It should be fairly simple to convert your T-SQL function to a Snowflake JavaScript function. Maybe you have to learn JavaScript on the way, though.
The skeleton of such a function can be:
CREATE OR REPLACE FUNCTION GetEasterSunday(Y FLOAT) RETURNS STRING LANGUAGE JAVASCRIPT AS
$$
var EpactCalc = (24 + 19 * (Y % 19)) % 30;
// more stuff here
var EasterMonth = 4, EasterDay = 21;
return Y + "-" + ("0" + EasterMonth).substr(-2) + "-" + ("0" + EasterDay).substr(-2);
$$;
SELECT GetEasterSunday(2019)::DATE;
It seems that T-SQL truncates values when you use integers. It does not take into count the fractional part at all.
In T-SQL it goes like this:
select -2 + 28 - (31 * (3 / 4)) as [EasterDay]
Returns: 26
In snowflake
select -2 + 28 - (31 * (3 / 4)) as [EasterDay]
Returns: 2.75
But if you truncate the value 3 / 4 = 0.75 , it gives same as T-SQL (because if you omit the fractional part it is trunc(0.75)=0:
select -2 + 28 - (31 * trunc(3 / 4)) as [EasterDay]
Returns: 26
So in T-SQL like this:
WITH years as(
SELECT 2009 AS [Year]
UNION ALL
SELECT yl.[Year] + 1 AS [Year]
FROM years yl
WHERE yl.[Year] + 1 <= YEAR(dateadd(year,5,GETDATE()))
),
e AS
(
SELECT d.[Year], [EasterDate] = CONVERT(DATE, RTRIM([Year]) + '0' + RTRIM([Month])
+ RIGHT('0' + RTRIM([Day]),2))
FROM (SELECT [Year],[Month], [Day] = DaysToSunday + 28 - (31 * ([Month] / 4))
FROM (SELECT [Year],[Month] = 3 + (DaysToSunday + 40) / 44, DaysToSunday
FROM (SELECT [Year],DaysToSunday = paschal - (([Year] + [Year] / 4 + paschal - 13) % 7 )
FROM (SELECT [Year],paschal = epact - (epact / 28)
FROM (SELECT years.[Year], epact = (24 + 19 * (years.[Year] % 19)) % 30 from years) AS epact) AS paschal) AS dts) AS m) AS d
)
select *
from(
SELECT [Year],[EasterDate], HolidayName = 'Easter Sunday' FROM e
UNION ALL SELECT [Year],DATEADD(DAY,-2,[EasterDate]), 'Good Friday' FROM e
UNION ALL SELECT [Year],DATEADD(DAY, 1,[EasterDate]), 'Easter Monday' FROM e
UNION ALL SELECT [Year],DATEADD(DAY, 39,[EasterDate]), 'Ascension Day' FROM e --NOTE! 1973–1991 this was moved to always to previous saturday!
UNION ALL SELECT [Year],DATEADD(DAY, 49,[EasterDate]), 'Whit Sunday' FROM e --NOTE! 7th sunday from Easter Sunday
) easter
order by easter.[Year],[EasterDate]
In Snowflake like this
WITH years as(
--select distinct "YearNumber" as "Year" FROM dm.D_DAY
select year(dateadd(year, seq4(), dateadd(year,-13,current_date()))) as "Year"
from
table(generator(rowcount => 19))
),
e AS
(
SELECT "EasterYear","EasterMonth", "EasterDay","DaysToSunday","epact","paschal",
DATE_FROM_PARTS("EasterYear", "EasterMonth", "EasterDay") as "EasterDate"
FROM (SELECT "EasterYear","epact","paschal","EasterMonth" AS "EasterMonth","DaysToSunday", "DaysToSunday" + 28 - (31 * trunc("EasterMonth" / 4)) as "EasterDay"
FROM (SELECT "EasterYear","epact","paschal",trunc(3 + ("DaysToSunday" + 40) / 44,0) as "EasterMonth", "DaysToSunday"
FROM (SELECT "EasterYear","epact","paschal","paschal" - trunc(mod(("EasterYear" + "EasterYear" / 4 + "paschal" - 13),7 )) as "DaysToSunday"
FROM (SELECT "EasterYear","epact","epact" - trunc("epact" / 28) as "paschal"
FROM (SELECT years."Year" as "EasterYear", mod((24 + (19 * mod(years."Year",19))),30) as "epact" from years) AS "epact") AS "paschal") AS "dts") AS "m") AS d
)
select *
from( SELECT "EasterYear","EasterDate",'Easter Sunday' as "HolidayName" FROM e
UNION ALL SELECT "EasterYear",DATEADD(DAY,-2,"EasterDate"), 'Good Friday' FROM e
UNION ALL SELECT "EasterYear",DATEADD(DAY, 1,"EasterDate"), 'Easter Monday' FROM e
UNION ALL SELECT "EasterYear",DATEADD(DAY, 39,"EasterDate"), 'Ascension Day' FROM e --NOTE! 1973–1991 this was moved to always to previous saturday!
UNION ALL SELECT "EasterYear",DATEADD(DAY, 49,"EasterDate"), 'Whit Sunday' FROM e --NOTE! 7th sunday from Easter Sunday
) easter
order by easter."EasterYear","EasterDate"
I am trying to create a script that identifies the date range of the previous month (Cell G9 is today's date). It is working; however, the output is formatting the date much differently than I am intending. The [J36] output is "01/08" when it should be "08/01" and the [J37] output is "6:11:37 AM" (interpretting it as a time), when it should be "08/31". I am only coding for the output in particular cells because I want to see that it is working. However, it is the variable I want to format, not the cell. I'm going to be using the startdate and enddate variable in future code and those variables on their own won't be visable in any cells.
I attempted to use the format(startdate,mm/dd), but I got an overflow error.
Here is my script:
Dim thismonth As Integer
thismonth = Month(Sheet1.[G9])
Dim startdate As Date
Dim enddate As Date
Select Case thismonth
Case 1
startdate = 12 / 1
enddate = 12 / 31
Case 2
startdate = 1 / 1
enddate = 1 / 31
Case 3
startdate = 2 / 1
enddate = 2 / 28
Case 4
startdate = 3 / 1
enddate = 3 / 31
Case 5
startdate = 4 / 1
enddate = 4 / 30
Case 6
startdate = 5 / 1
enddate = 5 / 31
Case 7
startdate = 6 / 1
enddate = 6 / 30
Case 8
startdate = 7 / 1
enddate = 7 / 31
Case 9
startdate = 8 / 1
enddate = 8 / 31
Case 10
startdate = 9 / 1
enddate = 9 / 31
Case 11
startdate = 10 / 1
enddate = 10 / 31
Case 12
startdate = 11 / 1
enddate = 11 / 30
End Select
Sheet1.[J36] = startdate
Sheet1.[J37] = enddate
How about a much simpler solution:
Dim startDate as Date
Dim endDate as Date
startDate = Application.WorksheetFunction.EOMONTH(Sheet1.[G9],-2) + 1
endDate = Application.WorksheetFunction.EOMONTH(Sheet1.[G9],-1)
You can get rid of VBA altogether and write this worksheet formula directly in J36 and J37 respectively.
J36: =EOMONTH(G9,-2)+1
J37: =EOMONTH(G9,-1)
Note: this is for Canadian Holidays:
I need to populate a table with Canadian business holidays.
I have the following query that sets up business holiday dates and also considers the movement of the holiday to the next weekday in the event of the holiday occurring on a weekend. ie if the holiday occurs on Sunday then Monday becomes a holiday. I also identify weekdays with a flag and add an English description of the day.
I add a few calculated columns to make the job a bit easier, these are removed again after all the data has been created. I cannot drop the table; the table maybe partly populated and I cannot overwrite existing data. This is the root of my problem. Because of the 'Were' the query is very slow.
The query I am using to populate the table is:
DECLARE #StartDate DATE = '20000101'
,#EndDate DATE = '21631231';
WITH N10(n)
AS (SELECT 1
FROM ( VALUES ( 0), ( 1), ( 2), ( 3), ( 4), ( 5), ( 6), ( 7), ( 8), ( 9) ) v (n)),
N100(n)
AS (SELECT 1
FROM N10
,N10 n),
N10000(n)
AS (SELECT 1
FROM N100
,N100 n),
N100000(n)
AS (SELECT 1
FROM N10
,N10000 n),
N AS (SELECT TOP (DATEDIFF(DAY,#StartDate,#EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
FROM N100000)
INSERT INTO [dbo].[BusinessCalendarDetails2] (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
IsHoliday)
(SELECT InsertDate,1,CURRENT_TIMESTAMP,1,1,0
FROM N
CROSS APPLY (SELECT DATEADD (DAY,n,#StartDate)) d (InsertDate)
WHERE NOT EXISTS ( SELECT 1 FROM [BusinessCalendarDetails2]
WHERE [BusinessDate] = InsertDate ));
This works but the problem is the 'WHERE' to identify if the date already exists in the table is severely slowing it down (14 mins). So I am wondering if someone has a faster solution for identifying already existing dates?
Thanks in advance for your assistance.
The following is the entire script, it works fairly well; others may find it useful.
-- Populate table with business holidays
-- If the table is missing add it
IF OBJECT_ID('BusinessCalendarDetails2') IS NULL
BEGIN
CREATE TABLE dbo.BusinessCalendarDetails2 (
Id BIGINT IDENTITY(1,1)
NOT NULL
,BusinessDate [DATE] NOT NULL
,Day AS DAY(BusinessDate)
,Week AS DATEPART(WEEK,BusinessDate)
,[Month] AS MONTH(BusinessDate)
,Quarter AS DATEPART(QUARTER,BusinessDate)
,[Year] AS YEAR(BusinessDate)
,DayOfWeek AS DATEPART(WEEKDAY,BusinessDate)
,CreatedById BIGINT NOT NULL
,CreatedTime DATETIMEOFFSET(7) NOT NULL
,UpdatedById BIGINT NULL
,UpdatedTime DATETIMEOFFSET(7) NULL
,BusinessCalendarId BIGINT NOT NULL
,RowVersion TIMESTAMP NOT NULL
,IsWeekday BIT NOT NULL
,IsHoliday BIT NOT NULL
,Description NVARCHAR(50)
,PRIMARY KEY CLUSTERED (Id ASC)
WITH (PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY])
ON [PRIMARY];
END;
-- Check if this col exists,
IF COL_LENGTH('dbo.BusinessCalendarDetails2','Day') IS NULL
BEGIN
ALTER TABLE dbo.BusinessCalendarDetails2
ADD -- Add calculated fields
Day AS DAY(BusinessDate), Week AS DATEPART(WEEK, BusinessDate), [Month] AS MONTH(BusinessDate), Quarter AS DATEPART(QUARTER, BusinessDate), [Year] AS YEAR(BusinessDate), DayOfWeek AS DATEPART(WEEKDAY, BusinessDate);
END;
GO
-- Date range to populate
DECLARE #StartDate DATE= '20000101' ,#EndDate DATE= '21631231';
WITH N10(n)
AS (SELECT 1
FROM ( VALUES ( 0 ), ( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 ), ( 6 ), ( 7 ), ( 8 ), ( 9 ) ) AS v (n)),
N100(n)
AS (SELECT 1
FROM N10
,N10 AS n),
N10000(n)
AS (SELECT 1
FROM N100
,N100 AS n),
N100000(n)
AS (SELECT 1
FROM N10
,N10000 AS n),
N AS (SELECT TOP (DATEDIFF(DAY,#StartDate,#EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
FROM N100000)
INSERT INTO dbo.BusinessCalendarDetails2 (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
IsHoliday)
(SELECT InsertDate,1,CURRENT_TIMESTAMP,1,1,0
FROM N
CROSS APPLY (SELECT DATEADD (DAY,n,#StartDate)) AS d (InsertDate)
WHERE NOT EXISTS ( SELECT 1
FROM BusinessCalendarDetails2
WHERE BusinessDate = InsertDate ));
-- Set Descriptions
UPDATE dbo.BusinessCalendarDetails2
SET Description = DATENAME(dw,BusinessDate)
FROM dbo.BusinessCalendarDetails2;
-- Set weekdays
UPDATE dbo.BusinessCalendarDetails2
SET IsWeekday = 0,Description = Description + ' Weekend'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE DATEPART(WEEKDAY,c1.BusinessDate) IN (1,7);
-- New Years Day
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' New Years Day'
FROM dbo.BusinessCalendarDetails2
WHERE BusinessCalendarDetails2.[Month] = 1
AND (SELECT CASE (##DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-01-01')) % 7
WHEN 0 THEN 3 -- SAT
WHEN 1 THEN 2 -- Sunday
ELSE 1
END) = BusinessCalendarDetails2.Day;
-- Family Day Day -- 3rd Monday in February
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Family Day'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE c1.[Month] = 2
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 15 AND 21;
-- Canada Day
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Canada Day'
FROM dbo.BusinessCalendarDetails2
WHERE BusinessCalendarDetails2.[Month] = 7
AND (SELECT CASE (##DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-07-01')) % 7
WHEN 0 THEN 3 -- SAT
WHEN 1 THEN 2 -- Sunday
ELSE 1
END) = BusinessCalendarDetails2.Day;
-- Civic Holiday
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Civic Holiday'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE c1.[Month] = 8
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 1 AND 7;
-- Good Friday
UPDATE BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Good Friday'
FROM dbo.BusinessCalendarDetails2 AS dimdate
CROSS APPLY (SELECT dimdate.Year AS y) _y
CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
CROSS APPLY (SELECT i - j AS el) _el
CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
WHERE dimdate.BusinessDate = DATEADD(DAY,-2,EasterSunday);
-- Easter Sunday
UPDATE BusinessCalendarDetails2
SET IsHoliday = 0,Description = Description + ' Easter Sunday'
FROM dbo.BusinessCalendarDetails2 AS dimdate
CROSS APPLY (SELECT dimdate.Year AS y) _y
CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
CROSS APPLY (SELECT i - j AS el) _el
CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
WHERE dimdate.BusinessDate = EasterSunday;
-- Labour Day -- first Monday of September
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Labour Day'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE c1.[Month] = 9
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 1 AND 7;
-- Set Thanksgiving
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Thanksgiving'
FROM dbo.BusinessCalendarDetails2 AS c1
WHERE c1.[Month] = 10
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 8 AND 14;
---- Christmas
UPDATE dbo.BusinessCalendarDetails2
SET IsHoliday = 1,Description = Description + ' Christmas Holidays'
FROM dbo.BusinessCalendarDetails2
WHERE [Month] = 12
AND Day BETWEEN 26 AND 28
AND (DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-25') IN (1,7) -- Is Christmas on a weekend this year
OR DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-26') IN (1,7)) -- Is Boxingday on a weekend this year
AND DATEPART(dw,BusinessDate) <> 4 -- NOT ON Wednesday!
AND DATEPART(DAY,(SELECT CASE (##DATEFIRST + DATEPART(dw,BusinessDate)) % 7
WHEN 0 THEN DATEADD(DAY,2,BusinessDate) -- Saturday
WHEN 1 THEN DATEADD(DAY,1,BusinessDate) -- Sunday
ELSE BusinessDate
END AS Weekday)) BETWEEN 26
AND 28
OR [Month] = 12 -- Is christmas a week day?
AND Day BETWEEN 25 AND 26
AND IsWeekday = 1;
GO
-- Return table to orig state
ALTER TABLE dbo.BusinessCalendarDetails2 DROP COLUMN Day, Week, [Month], Quarter, [Year], DayOfWeek;
If there's no index on BusinessDate and the table is big enough, that would certainly slow things down. If it is indexed, you may need to rebuild/reorganize it, or even just redo the statistics.
Otherwise, depending on your disks, it may be quicker to insert the distinct BusinessDate column values into a temp table with an index on it and compare against that.
It could be many things. If you haven't already, you should look at the ACTUAL execution plan and statistics for your query to get a better idea of what exactly is causing the issue.
This is the final answer using a Temp table it runs much faster
-- Populate table with business holidays
CREATE TABLE #BusCalDet (
Id BIGINT IDENTITY(1,1)
NOT NULL
,BusinessDate [DATE] NOT NULL
,Day AS DAY(BusinessDate)
,Week AS DATEPART(WEEK,BusinessDate)
,[Month] AS MONTH(BusinessDate)
,Quarter AS DATEPART(QUARTER,BusinessDate)
,[Year] AS YEAR(BusinessDate)
,DayOfWeek AS DATEPART(WEEKDAY,BusinessDate)
,CreatedById BIGINT NOT NULL
,CreatedTime DATETIMEOFFSET(7) NOT NULL
,UpdatedById BIGINT NULL
,UpdatedTime DATETIMEOFFSET(7) NULL
,BusinessCalendarId BIGINT NOT NULL
,RowVersion TIMESTAMP NOT NULL
,IsWeekday BIT NOT NULL
,IsHoliday BIT NOT NULL
,Description NVARCHAR(50)
,PRIMARY KEY CLUSTERED (Id ASC)
WITH (PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY])
ON [PRIMARY];
-- Date range to populate
DECLARE #StartDate DATE= '20000101'
,#EndDate DATE= '21631231';
WITH N10(n)
AS (SELECT 1
FROM ( VALUES ( 0 ), ( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 ), ( 6 ), ( 7 ), ( 8 ), ( 9 ) ) AS v (n)),
N100(n)
AS (SELECT 1
FROM N10
,N10 AS n),
N10000(n)
AS (SELECT 1
FROM N100
,N100 AS n),
N100000(n)
AS (SELECT 1
FROM N10
,N10000 AS n),
N AS (SELECT TOP (DATEDIFF(DAY,#StartDate,#EndDate) + 1)
n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1
FROM N100000)
INSERT INTO #BusCalDet (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,IsHoliday)
SELECT InsertDate,1,CURRENT_TIMESTAMP,1,1,0
FROM N
CROSS APPLY (SELECT DATEADD (DAY,n,#StartDate)) AS d (InsertDate);
-- Set Descriptions
UPDATE #BusCalDet
SET Description = DATENAME(dw,BusinessDate)
FROM #BusCalDet;
-- Set weekdays
UPDATE #BusCalDet
SET IsWeekday = 0,Description = Description + ' Weekend'
FROM #BusCalDet AS c1
WHERE DATEPART(WEEKDAY,c1.BusinessDate) IN (1,7);
-- New Years Day
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' New Years Day'
FROM #BusCalDet
WHERE #BusCalDet.[Month] = 1
AND (SELECT CASE (##DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-01-01'))
% 7
WHEN 0 THEN 3 -- SAT
WHEN 1 THEN 2 -- Sunday
ELSE 1
END) = #BusCalDet.Day;
-- Family Day Day -- 3rd Monday in February
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Family Day'
FROM #BusCalDet AS c1
WHERE c1.[Month] = 2
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 15 AND 21;
-- Canada Day
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Canada Day'
FROM #BusCalDet
WHERE #BusCalDet.[Month] = 7
AND (SELECT CASE (##DATEFIRST + DATEPART(DW,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-07-01'))
% 7
WHEN 0 THEN 3 -- SAT
WHEN 1 THEN 2 -- Sunday
ELSE 1
END) = #BusCalDet.Day;
-- Civic Holiday
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Civic Holiday'
FROM #BusCalDet AS c1
WHERE c1.[Month] = 8
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 1 AND 7;
-- Good Friday
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Good Friday'
FROM #BusCalDet AS dimdate
CROSS APPLY (SELECT dimdate.Year AS y) _y
CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
CROSS APPLY (SELECT i - j AS el) _el
CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
WHERE dimdate.BusinessDate = DATEADD(DAY,-2,EasterSunday);
-- Easter Sunday
UPDATE #BusCalDet
SET IsHoliday = 0,Description = Description + ' Easter Sunday'
FROM #BusCalDet AS dimdate
CROSS APPLY (SELECT dimdate.Year AS y) _y
CROSS APPLY (SELECT y / 100 AS c,y - 19 * y / 19 AS n) _nc
CROSS APPLY (SELECT (c - 17) / 25 AS k) _k
CROSS APPLY (SELECT c - c / 4 - (c - k) / 3 + 19 * n + 15 AS i1) _i1
CROSS APPLY (SELECT i1 - 30 * i1 / 30 AS i2) _i2
CROSS APPLY (SELECT i2 - (i2 / 28) * (1 - (i2 / 28) * 29 / (i2 + 1) * (21 - n) / 11) AS i) _i
CROSS APPLY (SELECT y + y / 4 + i + 2 - c + c / 4 AS j1) _j1
CROSS APPLY (SELECT j1 - 7 * j1 / 7 AS j) _j
CROSS APPLY (SELECT i - j AS el) _el
CROSS APPLY (SELECT 3 + (el + 40) / 44 AS m) _m
CROSS APPLY (SELECT el + 28 - 31 * m / 4 AS d) _d
CROSS APPLY (SELECT DATEFROMPARTS (y,m,d) AS EasterSunday) _Easter
WHERE dimdate.BusinessDate = EasterSunday;
-- Labour Day -- first Monday of September
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Labour Day'
FROM #BusCalDet AS c1
WHERE c1.[Month] = 9
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 1 AND 7;
-- Set Thanksgiving
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Thanksgiving'
FROM #BusCalDet AS c1
WHERE c1.[Month] = 10
AND c1.DayOfWeek = 2
AND c1.Day BETWEEN 8 AND 14;
---- Christmas
UPDATE #BusCalDet
SET IsHoliday = 1,Description = Description + ' Christmas Holidays'
FROM #BusCalDet
WHERE [Month] = 12
AND Day BETWEEN 26 AND 28
AND (DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-25') IN (1,7) -- Is Christmas on a weekend this year
OR DATEPART(WEEKDAY,CAST(DATEPART(YEAR,BusinessDate) AS VARCHAR) + '-12-26') IN (1,7)) -- Is Boxingday on a weekend this year
AND DATEPART(dw,BusinessDate) <> 4 -- NOT ON Wednesday!
AND DATEPART(DAY,(SELECT CASE (##DATEFIRST + DATEPART(dw,BusinessDate)) % 7
WHEN 0 THEN DATEADD(DAY,2,BusinessDate) -- Saturday
WHEN 1 THEN DATEADD(DAY,1,BusinessDate) -- Sunday
ELSE BusinessDate
END AS Weekday)) BETWEEN 26
AND 28
OR [Month] = 12 -- Is christmas a week day?
AND Day BETWEEN 25 AND 26
AND IsWeekday = 1;
-- Match orig table Schema
ALTER TABLE #BusCalDet DROP COLUMN Day, Week, [Month], Quarter, [Year], DayOfWeek;
-- Insert in to the main table'
INSERT INTO dbo.BusinessCalendarDetails (BusinessDate,CreatedById,CreatedTime,BusinessCalendarId,IsWeekday,
IsHoliday,[Description])
-- Find Rows that are missing
SELECT DT.BusinessDate,DT.CreatedById,DT.CreatedTime,DT.BusinessCalendarId,DT.IsWeekday,DT.IsHoliday,
DT.[Description]
FROM #BusCalDet DT
LEFT JOIN BusinessCalendarDetails ON BusinessCalendarDetails.BusinessDate = DT.BusinessDate
WHERE BusinessCalendarDetails.Id IS NULL
ORDER BY DT.BusinessDate;-- id
I am very new in SQL (started just 3 days back) and I got an issue and would hope someone is able to assist me.
I have a query that creates date loops to return me the week number based on the passed in dates. The idea here is to pass loop through the days, months and years and return me a week value. But because of these, I have been getting multiple entries of the same result.
For eg. I am getting:
period_wk_key period_yr_key period_week period_week_day period_week_full_desc
--------------- -------------- ------------ -------------- -----------------------
200001 2000 1 6 2000 WEEK 1
200001 2000 1 6 2000 WEEK 1
200001 2000 1 6 2000 WEEK 1
. . . . .
200002 2000 2 6 2000 WEEK 2
200002 2000 2 6 2000 WEEK 2
200002 2000 2 6 2000 WEEK 2
. . . . .
200003 2000 3 6 2000 WEEK 3
each period_wk_key is returning me 7 similar rows which is not what I wanted.
The ideal situation should be:
period_wk_key period_yr_key period_week period_week_day period_week_full_desc
--------------- -------------- ------------ -------------- -----------------------
200001 2000 1 6 2000 WEEK 1
200002 2000 2 6 2000 WEEK 2
200003 2000 3 6 2000 WEEK 3
200004 2000 4 6 2000 WEEK 4
I need to know a way to limit the number of output from a loop so that I can have distinct records instead of multiple similar rows. I have attached a code below. It maybe crap to many of you but this is just my 3rd day beginning scripting. Much thanks for any help rendered.
DECLARE #iStartYear INT
SET #iStartYear = 2000
DECLARE #iEndYear INT
SET #iEndYear = 2030
DECLARE #iMth INT
SET #iMth = 1
DECLARE #iDay INT
SET #iDay = 1
DECLARE #iWeek INT
`DECLARE #StartDate DATETIME
WHILE (#iStartYear <= #iEndYear)
BEGIN
WHILE (#iMth <= 12)
BEGIN
WHILE (#iDay <= DATEDIFF(DAY,DATEADD(DAY, 0, DATEADD(m, ((#iStartYear - 1900) * 12) + #iMth - 1, 0)),DATEADD(DAY, 0, DATEADD(m, ((#iStartYear - 1900) * 12) + #iMth, 0)))))
BEGIN `
SET #iWeek = (DATEPART(dy, CONVERT(DATETIME, CONVERT(VARCHAR(4), #iStartYear) + '/' + CONVERT(VARCHAR(2), #iMth) + '/' + CONVERT(VARCHAR(2), #iDay))) - 1) / 7 + 1;
INSERT INTO dim_period_week (period_wk_key, period_yr_key, period_week, period_week_full_desc,start_date, end_date, period_week_day)
VALUES (
(SELECT CASE WHEN #iWeek < 10 THEN
CAST((#iStartYear) AS VARCHAR) + '0' + CAST((#iWeek) AS VARCHAR)
ELSE
CAST((#iStartYear) AS VARCHAR)+ CAST((#iWeek) AS VARCHAR)
END),
#iStartYear,
(SELECT (DATEPART(dy, CONVERT(DATETIME, CONVERT(VARCHAR(4), #iStartYear) + '/' + CONVERT(VARCHAR(2), #iMth) + '/' + CONVERT(VARCHAR(2), #iDay))) - 1) / 7 + 1),
(SELECT CAST((#iStartYear) AS VARCHAR) + ' ' + 'WEEK' + ' ' + CAST((#iWeek) AS VARCHAR)),
(SELECT DATEADD(wk,#iWeek-1, DATEADD(yy,#iStartYear-1900,0))), --START DATE
(SELECT DATEADD(wk,#iWeek, DATEADD(yy,#iStartYear-1900,0)) -1), --END DATE
(SELECT DATEDIFF(DAY, DATEADD(wk,#iWeek-1, DATEADD(yy,#iStartYear-1900,0)), DATEADD(wk,#iWeek, DATEADD(yy,#iStartYear-1900,0)) -1))
)
`SET #iDay = #iDay + 1
END
SET #iDay = 1
SET #iMth = #iMth + 1
END
SET #iMth = 1
SET #iStartYear = #iStartYear + 1
END
`
You are looping every day, but never storing at the day level, so you are doing 7 times as many INSERTs as needed. Add the following just above your INSERT statement
IF DATEPART(weekday, CAST(CAST(#iStartYear * 10000 + #iMth * 100 + #iDay AS VARCHAR(8)) AS datetime)) = 1
INSERT INTO dim_period_week ...
Below is a set based approach using a numbers table.
IF OBJECT_ID('tempdb.dbo.#Numbers') IS NOT NULL DROP TABLE #Numbers
GO
DECLARE #startDate DATETIME = '20000101'
SELECT
(a.Number * 256) + b.Number AS Number
INTO #Numbers
FROM
(SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255) a (Number),
(SELECT number FROM master..spt_values WHERE type = 'P' AND number <= 255) b (Number)
WHERE
(a.Number * 256) + b.Number BETWEEN 0 AND 1617
SELECT
WeekKey = CAST(YEAR(DATEADD(day, N.Number * 7, #startDate)) * 100 + DATEPART(week, DATEADD(day, N.Number * 7, #startDate)) AS VARCHAR(10)),
TheYear = YEAR(DATEADD(day, N.Number * 7, #startDate)),
TheWeek = DATEPART(week, DATEADD(day, N.Number * 7, #startDate)),
TheWeekDay= DATEPART(weekday, DATEADD(day, N.Number * 7, #startDate)),
WeekDetail= DATENAME(year, DATEADD(day, N.Number * 7, #startDate)) + ' WEEK ' + DATENAME(week, DATEADD(day, N.Number * 7, #startDate)),
StartDate = DATEADD(day, N.Number * 7, #startDate),
EndDate = DATEADD(day, 6, DATEADD(day, N.Number * 7, #startDate))
FROM #Numbers N
ORDER BY 1