Easter / Good Friday for Sowflake Date Dim - sql

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"

Related

Generating sequence skipping by 2 Oracle

I have the below query.But I want to tweak the output to skip 2 consecutive numbers on every cycle.
For example skip 1,2 in 2nd cycle . Skip 3,4 in 3rd cycle . Skip 5,6 in 4th cycle and so on.
Also, there will be 2 input variables (i) the number to be removed/added (ii) from which date.
Once this is passed the sequence will have to be recalculate from that date on wards.
I am using Oracle version 12.1
SELECT TRUNC(sysdate, 'Y') + level - 1 AS "date", MOD(level - 1, 10) + 1 AS col1
FROM dual
CONNECT BY level <= ADD_MONTHS(TRUNC(sysdate, 'Y'), 12) - TRUNC(sysdate, 'Y')
Date Col1 Expected Output
1/1/2022 1 1
1/2/2022 2 2
1/3/2022 3 3
1/4/2022 4 4
1/5/2022 5 5
1/6/2022 6 6
1/7/2022 7 7
1/8/2022 8 8
1/9/2022 9 9
1/10/2022 10 10
1/11/2022 1 3
1/12/2022 2 4
1/13/2022 3 5
1/14/2022 4 6
1/15/2022 5 7
1/16/2022 6 8
1/17/2022 7 9
1/18/2022 8 10
1/19/2022 9 1
1/20/2022 10 2
1/21/2022 1 5
1/22/2022 2 6
1/23/2022 3 7
1/24/2022 4 8
1/25/2022 5 9
1/26/2022 6 10
1/27/2022 7 1
1/28/2022 8 2
1/29/2022 9 3
1/30/2022 10 4
1/31/2022 1 7
2/1/2022 2 8
2/2/2022 3 9
2/3/2022 4 10
You can use some more arithmetical functions such as FLOOR() and CEIL() along with precomputing unmatched values within a subquery, and then filter out by the incremented values upto the currently existing expression's value of
ADD_MONTHS(TRUNC(sysdate, 'Y'), 12) - TRUNC(sysdate, 'Y')
such as
WITH d AS
(
SELECT SUM(CASE
WHEN MOD(FLOOR((level-.1)/10),6) = CEIL((MOD(level - 1, 10) + 1)/2)
THEN
0
ELSE
1
END)
OVER (ORDER BY level) AS rn,
ADD_MONTHS(TRUNC(sysdate, 'Y'), 12) - TRUNC(sysdate, 'Y') AS day_diff,
MOD(level - 1, 10) + 1 AS col1,
MOD(FLOOR((level-.1)/10),6) AS col2,
CEIL((MOD(level - 1, 10) + 1)/2) AS col3
FROM dual
CONNECT BY level <= (ADD_MONTHS(TRUNC(sysdate, 'Y'), 12) - TRUNC(sysdate, 'Y'))*2
)
SELECT TRUNC(sysdate, 'Y') + rn - 1 AS "date", col1
FROM d
WHERE col2 != col3
AND rn <= day_diff
ORDER BY rn
Demo
Wrote below . Thanks for your answer as well. I just made some columns in excel and wrote some functions to get it
WITH Y AS
(SELECT TRUNC(SYSDATE, 'Y') + LEVEL - 1 dt,
MOD(LEVEL - 1, 10) + 1 AS grpid,
LEVEL rnum
FROM dual
CONNECT BY LEVEL <=
ADD_MONTHS(TRUNC(SYSDATE, 'Y'), 12) - TRUNC(SYSDATE, 'Y')),
Z AS
(SELECT dt,
rnum,
COUNT(CASE
WHEN grpid = 1 THEN
1
END) OVER(ORDER BY dt) - 1 as grp
FROM y)
SELECT z.*,
REGEXP_REPLACE(CASE
WHEN MOD(rnum, grp * 10) = 1 THEN
MOD(grp + grp + 1, 10)
WHEN MOD(rnum, grp * 10) = 2 THEN
MOD(grp + grp + 2, 10)
ELSE
MOD(rnum - grp * (10 - 2), 10)
END,
'^0$',
10) output
FROM Z;

Case statement giving both values (the one in "then" and in "else" together) in Bigquery. What is wrong?

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

Calculate manual week number of year in Oracle SQL

i have a date column and along that i need to calculate another column in oracle for week number of they year, the weeks should be from sunday to saturday, starting first day of the year.
for example for current year
Week 1 : 1 Jan 2020 (Wednesday) - 4 Jan 2020(Saturday)
Week 2 : 5 Jan 2020 (Sunday) - 11 Jan 2020(Saturday)
. . . . .
Week 5 : 26 Jan 2020 (Sunday) - 1 Feb 2020 (Saturday)
and so on...
You need to write your own logic using a hierarchy query.
Something like the following:
SQL> SELECT WEEKNUMBER,
2 WEEK_START,
3 CASE WHEN WEEKNUMBER = 1 THEN FIRST_WEEKEND ELSE WEEK_START + 6 END AS WEEK_END
4 FROM
5 (SELECT
6 CASE
7 WHEN LEVEL = 1 THEN FIRST_DAY
8 ELSE FIRST_WEEKEND + ( LEVEL - 2 ) * 7 + 1
9 END AS WEEK_START,
10 FIRST_WEEKEND,
11 LEVEL AS WEEKNUMBER
12 FROM
13 ( SELECT
14 TRUNC(SYSDATE, 'YEAR') FIRST_DAY,
15 NEXT_DAY(TRUNC(SYSDATE, 'YEAR'), 'SATURDAY') FIRST_WEEKEND
16 FROM DUAL )
17 CONNECT BY
18 CASE WHEN LEVEL = 1 THEN FIRST_DAY
19 ELSE FIRST_WEEKEND + ( LEVEL - 2 ) * 7 + 1
20 END < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 12));
WEEKNUMBER WEEK_STAR WEEK_END
---------- --------- ---------
1 01-JAN-20 04-JAN-20
2 05-JAN-20 11-JAN-20
3 12-JAN-20 18-JAN-20
4 19-JAN-20 25-JAN-20
5 26-JAN-20 01-FEB-20
6 02-FEB-20 08-FEB-20
7 09-FEB-20 15-FEB-20
8 16-FEB-20 22-FEB-20
9 23-FEB-20 29-FEB-20
10 01-MAR-20 07-MAR-20
11 08-MAR-20 14-MAR-20
.......
.......
53 27-DEC-20 02-JAN-21
Cheers!!
One other option would be
with t as
(
select trunc(sysdate,'yyyy')+level-1 as day, to_char(trunc(sysdate,'yyyy')+level-1,'DY','NLS_DATE_LANGUAGE=AMERICAN') as weekday, level - 1 as lvl
from dual
connect by level <= 366
), t1 as
(
select case
when lvl = 0 then
day
else
case
when weekday = 'SUN' then
day
end
end as day1,
lvl
from t
), t2 as
(
select case
when weekday = 'SAT' then
day
end as day2,
lvl
from t
)
select concat( 'Week ', wk1) as week, day1, day2
from
(
select row_number() over (order by lvl) wk1, day1
from t1 where day1 is not null ) t1
left join
(
select row_number() over (order by lvl) wk2, day2
from t2 where day2 is not null ) t2
on wk2 = wk1
by using select .. from dual connect by level syntax and case..when expression to scan all the current year.
Demo

SQL Server - Set Canadian Business Holiday Date

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

How to get days (monday ,tuesday.....) between two different dates?

I need to show a report, which will show weekly report for training session.. from Monday to Saturday some time Sunday with the number of participants per day.
i want sql query to complete this task.
eg.
code days mon tues Wed thurs fri sat sun
10001 3 22 22 22 0 0 0 0
10002 5 10 10 10 10 10 0 0 and so on....
Here no. of participants are same for all days of that training session.
Updated Query
.....................my query as per #competent_tech suggestion .........................
CREATE VIEW SessionDOW
AS
SELECT tcapp_id,title,
case when programtype=1 then 'YP' else 'SP' end programtype,
case when NonResidential=2 then 'Res' else 'Non Res.' end nonresidential,duration,
fromdate,TODATE,
Training_Calendar.Participents,
StartDOW = DATEPART(weekday, FromDate) ,
EndDOW = DATEPART(weekday, FromDate)+Duration -1,
weekenddate=DATEPART(d,CONVERT(date,'12-12-2011'))+6
FROM Training_Calendar
GO
select * from sessiondow
CREATE VIEW SessionDays
AS
SELECT SessionDOW.*,
MON = CASE WHEN (2 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 2 + 7) THEN Participents ELSE 0 END ,
TUE = CASE WHEN (3 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 3 + 7) THEN Participents ELSE 0 END ,
WED = CASE WHEN (4 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 4 + 7) THEN Participents ELSE 0 END ,
THURS = CASE WHEN (5 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 5 + 7) THEN Participents ELSE 0 END ,
FRI = CASE WHEN (6 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 6 + 7) THEN Participents ELSE 0 END ,
SAT = CASE WHEN (7 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 0 + 7) THEN Participents ELSE 0 END,
SUN = CASE WHEN (1 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 1 + 7) THEN Participents ELSE 0 END
FROM SessionDOW
GO
SELECT e.*
FROM SessionDays e INNER JOIN Training_Calendar a ON e.tcapp_id = a.TCApp_Id
GROUP BY e.TCApp_Id,e.title,
e.programtype,
e.nonresidential,e.duration,
e.Participents,
e.StartDOW ,
e.EndDOW,sun,MON,tue,wed,thurs,fri,SAT,e.FromDate,e.todate,e.weekenddate
which give me following result
tcapp_id title programtype nonresidential duration fromdate TODATE Participents StartDOW EndDOW weekenddate MON TUE WED THURS FRI SAT SUN
2011-12/CHD-10001 testing SP Res 10 2011-12-05 2011-12-14 60 2 11 18 60 60 60 60 60 60 60
2011-12/CIT-10001 Training of Palak Technical Officer YP Res 2 2011-12-13 2011-12-14 45 3 4 18 0 45 45 0 0 0 0
2011-12/SIRD-10001 Induction Level Training Programme for JE's of MESCT YP Res 4 2011-12-12 2011-12-15 25 2 5 18 25 25 25 25 0 0 0
2011-12/SIUD-10001 Extempore Leadership Skills Workshop SP Non Res. 5 2011-12-14 2011-12-18 35 4 8 18 0 0 35 35 35 35 35
2011-12/SIUD-10002 fdgd YP Res 6 2011-12-15 2011-12-20 80 5 10 18 80 80 0 80 80 80 80
here week start from 12th dec and end to 18th dec but training session start from 5th dec and ends at 14th dec but still next few days(15,16,17,18) showing participants value. please help.
If this is an SQL Server database, then your query would be something along the lines of:
SELECT CODE ,
SUM(1) AS DAYS ,
SUN = SUM(CASE WHEN DATEPART(weekday, TimeStamp) = 0 THEN 1 ELSE 0 END) ,
MON = SUM(CASE WHEN DATEPART(weekday, TimeStamp) = 1 THEN 1 ELSE 0 END) ,
TUE = SUM(CASE WHEN DATEPART(weekday, TimeStamp) = 2 THEN 1 ELSE 0 END) ,
WED = SUM(CASE WHEN DATEPART(weekday, TimeStamp) = 3 THEN 1 ELSE 0 END) ,
THURS = SUM(CASE WHEN DATEPART(weekday, TimeStamp) = 4 THEN 1 ELSE 0 END) ,
FRI = SUM(CASE WHEN DATEPART(weekday, TimeStamp) = 5 THEN 1 ELSE 0 END) ,
SAT = SUM(CASE WHEN DATEPART(weekday, TimeStamp) = 6 THEN 1 ELSE 0 END)
FROM Event
GROUP BY code
Where Timestamp is the name of the column containing the date of the training and Event is the name of the table.
Update
The above query assumes that one record exists in the database for each student for each day they attend a class.
However, if the database design is such that there is one record for the session that specifies a start date and a number of days and another table where attendees are registered for a given session, then the query becomes a bit different.
Assuming the following rough table structure:
CREATE TABLE Sessions
(
code INT NOT NULL IDENTITY(1, 1) ,
date DATETIME NOT NULL ,
DAYS INT NOT NULL ,
PRIMARY KEY (code)
)
CREATE TABLE Attendees
(
id INT NOT NULL IDENTITY(1, 1) ,
code INT NOT NULL ,
NAME NVARCHAR(100)
PRIMARY KEY (id) ,
FOREIGN KEY (code) REFERENCES sessions (code)
)
We first create a view to calculate the start and end day of week (this makes ensuing views easier to understand):
CREATE VIEW SessionDOW
AS
SELECT CODE ,
DAYS ,
StartDOW = DATEPART(weekday, date) ,
EndDOW = DATEPART(weekday, date) + DAYS
FROM Sessions
GO
We then create a view to record 1 for each day of the week that the session is running. Note that this view takes into account a session that spans two weeks:
CREATE VIEW SessionDays
AS
SELECT CODE ,
DAYS ,
SUN = CASE WHEN (0 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 0 + 7) THEN 1 ELSE 0 END ,
MON = CASE WHEN (1 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 1 + 7) THEN 1 ELSE 0 END ,
TUE = CASE WHEN (2 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 2 + 7) THEN 1 ELSE 0 END ,
WED = CASE WHEN (3 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 3 + 7) THEN 1 ELSE 0 END ,
THURS = CASE WHEN (4 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 4 + 7) THEN 1 ELSE 0 END ,
FRI = CASE WHEN (5 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 5 + 7) THEN 1 ELSE 0 END ,
SAT = CASE WHEN (6 BETWEEN StartDOW AND EndDOW) OR (EndDow >= 6 + 7) THEN 1 ELSE 0 END
FROM SessionDOW
GO
Finally, we join the sessiondays count with the students attending each session and add up the values:
SELECT e.CODE ,
MAX(DAYS) AS DAYS ,
SUN = SUM(SUN) ,
MON = SUM(MON) ,
TUE = SUM(TUE) ,
WED = SUM(WED) ,
THURS = SUM(THURS) ,
FRI = SUM(FRI) ,
SAT = SUM(SAT)
FROM SessionDays e INNER JOIN Attendees a ON e.code = a.code
GROUP BY e.code