Calendar table for Data Warehouse - sql-server

For my data warehouse, I am creating a calendar table as follows:
SET NOCOUNT ON
DROP Table dbo.Calendar
GO
Create Table dbo.Calendar
(
CalendarId Integer NOT NULL,
DateValue Date NOT NULL,
DayNumberOfWeek Integer NOT NULL,
NameOfDay VarChar (10) NOT NULL,
NameOfMonth VarChar (10) NOT NULL,
WeekOfYear Integer NOT NULL,
JulianDay Integer NOT NULL,
USAIsBankHoliday Bit NOT NULL,
USADayName VarChar (100) NULL,
)
ALTER TABLE dbo.Calendar ADD CONSTRAINT
DF_Calendar_USAIsBankHoliday DEFAULT 0 FOR USAIsBankHoliday
GO
ALTER TABLE dbo.Calendar ADD CONSTRAINT
DF_Calendar_USADayName DEFAULT '' FOR USADayName
GO
Declare #StartDate DateTime = '01/01/2000'
Declare #EndDate DateTime = '01/01/2020'
While #StartDate < #EndDate
Begin
INSERT INTO dbo.Calendar
(
CalendarId,
DateValue,
WeekOfYear,
DayNumberOfWeek,
NameOfDay,
NameOfMonth,
JulianDay
)
Values
(
YEAR (#StartDate) * 10000 + MONTH (#StartDate) * 100 + Day (#StartDate), --CalendarId
#StartDate, -- DateValue
DATEPART (ww, #StartDate), -- WeekOfYear
DATEPART (dw, #StartDate), -- DayNumberOfWeek
DATENAME (dw, #StartDate), -- NameOfDay
DATENAME (M, #StartDate), -- NameOfMonth
DATEPART (dy, #StartDate) -- JulianDay
)
Set #StartDate += 1
End
--=========================== Weekends
-- saturday and sunday
UPDATE dbo.Calendar SET USAIsBankHoliday = 1, USADayName += 'Weekend, ' WHERE DayNumberOfWeek IN (1, 7)
--=========================== Bank Holidays
-- new years day
UPDATE dbo.Calendar SET USAIsBankHoliday = 1, USADayName += 'New Year''s Day, ' WHERE (CalendarId % 2000) IN (101)
-- memorial day (last Monday in May)
UPDATE dbo.Calendar
SET USAIsBankHoliday = 1,
USADayName += 'Memorial Day, '
WHERE 1=1
AND CalendarId IN
(
SELECT MAX (CalendarId)
FROM dbo.Calendar
WHERE MONTH (DateValue) = 5
AND DATEPART (DW, DateValue)=2
GROUP BY YEAR (datevalue)
)
-- independence day
UPDATE dbo.Calendar SET USAIsBankHoliday = 1, USADayName += 'Independence Day, ' WHERE (CalendarId % 2000) IN (704)
-- labor day (first Monday in September)
UPDATE dbo.Calendar
SET USAIsBankHoliday = 1,
USADayName += 'Labor Day, '
WHERE 1=1
AND CalendarId IN
(
SELECT MIN (CalendarId)
FROM dbo.Calendar
WHERE MONTH (DateValue) = 9
AND DATEPART (DW, DateValue)=2
GROUP BY YEAR (datevalue)
)
-- thanksgiving day (fourth Thursday in November)
UPDATE dbo.Calendar
SET USAIsBankHoliday = 1,
USADayName += 'Thanksgiving Day, '
WHERE 1=1
AND CalendarId IN
(
SELECT Max (CalendarId)-2
FROM dbo.Calendar
WHERE MONTH (DateValue) = 11
AND DATEPART (DW, DateValue)=7
GROUP BY YEAR (datevalue)
)
-- christmas
UPDATE dbo.Calendar SET USAIsBankHoliday = 1, USADayName += 'Christmas Day, ' WHERE (CalendarId % 2000) IN (1225)
--=========================== Other named days
-- new years eve
UPDATE dbo.Calendar SET USADayName += 'New Year''s Eve, ' WHERE (CalendarId % 2000) IN (1231)
-- black friday (day after thanksgiving day)
UPDATE dbo.Calendar SET USADayName += 'Black Friday, ' WHERE CalendarId IN (SELECT CalendarId+1 From dbo.Calendar Where USADayName like '%Thanksgiving%')
-- christmas eve
UPDATE dbo.Calendar SET USADayName += 'Christmas Eve, ' WHERE (CalendarId % 2000) IN (1224)
-- boxing day
UPDATE dbo.Calendar SET USADayName += 'Boxing Day, ' WHERE (CalendarId % 2000) IN (1226)
--=========================== Remove trailing comma
UPDATE dbo.Calendar SET USADayName = SubString (USADayName, 1, LEN (USADayName) -1) WHERE LEN (USADayName) > 2
SELECT * FROM dbo.Calendar
Here is the output of this command
I have seen similar structures implemented in various flavours by data architects.
My question is: What other data warehousing / dimension style useful information can I add to this table structure?

Quarter
Year
Financial/Accounting Year
Financial/Accounting Quarter
isWeekend
isWeekday
isWorkDay
WeekId (weeks since start of year)
isLastDayofMonth
DaysSince (e.g. days since 1/1/2000)

This is my list of possible columns in calendar dimension:
Key
Date
Is Yesterday
Is Today
Is Tomorrow
Day of Year
Day of Halfyear
Day of Quarter
Day of Month
Day of Week
Day of Week Short Name
Day of Week Short Name CS
Day of Week Long Name
Day of Week Long Name CS
Days in Week
Days in Month
Days in Quarter
Days in Halfyear
Days in Year
Reverse Day of Week
Reversse Day of Month
Reverse Day of Quarter
Reverse Day of Halfyear
Reverse Day of Year
Is Last 7 days
Is Last 14 days
Is Last 30 days
Is Last 90 Days
Is Last 180 Days
Is Last 365 Days
Is Weekday
Is Weekend
Workday of Week
Workday of Month
Workday of Quarter
Workday of Halfyear
Workday of Year
Reverse Workday of Week
Reverse Workday of Month
Reverse Workday of Quarter
Reverse Workday of Halfyear
Reverse Workday of Year
Workdays in Week
Workdays in Month
Workdays in Quarter
Workdays in Halfyear
Workdays in Year
Is Last Workday in Week
Is Last Workday in Month
Is Workday
Is Holiday
Is Future
Is Past
Is Previous Month
Is Current Month
Is Following Month
Is Month to Date
Is Beginning of Month
Is End of Month
Is Past Month
Beginning of Month
End of Month
Month Number
Month Name Long
Month Name Long CS
Month Name Short
Month Name Short CS
Month of Quarter
Month of Halfyear
Is Previous Week
Is Current Week
Is Following Week
Is Week to Date
Is Beginning of Week
Is End of Week
Is Past Week
Beginning of Week
End of Week
Week Number
Week Name Long
Week Name Short
Week of Month
Is Previous Quarter
Is Current Quarter
Is Following Quarter
Is Quarter to Date
Is Beginning of Quarter
Is End of Quarter
Is Past Quarter
Beginning of Quarter
End of Quarter
Quarter Number
Quarter Name Long
Quarter Name Long CS
Quarter Name Short
Is Previous Halfyear
Is Current Halfyear
Is Following Halfyear
Is Halfyear to Date
Is Beginning of Halfyear
Is End of Halfyear
Is Past Halfyear
Beginning of Halfyear
End of Halfyear
Halfyear Number
Halfyear Name Long
Halfyear Name Long CS
Halfyear Name Short
Is Previous Year
Is Current Year
Is Following Year
Is Year to Date
Is Beginning of Year
Is End of Year
Is Past Year
Beginning of Year
End of Year
Year Number
Year Name Long
Year Name Short
Year Quarter Text
Year Month Day
Year Halfyear
Year Quarter
Year Month
Year Day of Year
Is Leap Year
Distance in Days from Today
Distance in Working Days from Today
Distance in Calendar Weeks from Today
Distance in Calendar Months from Today
Distance in Calendar Quarters from Today
Distance in Calendar Halfyears from Today
Distance in Calendar Years from Today
Nth Day of Week in Month
Reverse Nth Day of Week in Month
I created interactive spreadsheet where you can create your own time dimension for PostgreSQL database.

Well Raj More, Its a nice Post and very helpful script for Creating a calender,
The Other fields which you can include in the same table may be-
1) QuateroftheYear
2) IsWeekend
3) IsWeekday

quarter of year
year_quarter (2013-3)
month of year
year_month (2013-08)
week of year
week of month
day of year
day of quarter
day of month
day of week

Related

How to do rolling weeks based on date?

I'm currently at a data analyst student job following an internship and I have do to reports based on the ticketing tool of the company, so, I'm using a pre-calculated table (the administrator have made pre-calculated tables based on his querys).
I have week table with all I need and I have to do a rolling 26 week report.
Because it is calculated that are historized and I don't have creation_date or end_date column.
I can't manage to do this can you help me with this ?
Actually, as a rolling query, if I have 18 weeks for 2021 I will need the 8 weeks last weeks of 2020.
I have this columns : Closed, Week, Month, Backlog... and I need it just for Closed.
I've tried this :
SELECT SUM(CLOSED), WEEK, MONTh, YEAR
FROM E_GROUPE_INTERVENANT_SEMAINE_HISTORY
WHERE MONTH >= Month(getdate())-6
AND YEAR <= YEAR(getdate())
EDIT : The weeks range values 1 trough 53 or 52 it depends on the year, the weeks are weeks of month, I've tried this
SELECT SUM(CLOSED), WEEK, MONTH, YEAR
FROM E_GROUPE_INTERVENANT_SEMAINE_HISTORY
WHERE
YEAR(DATEADD(WEEK, -26, GETDATE())) = YEAR(GETDATE()) - 1
AND
YEAR = YEAR(GETDATE())
AND
MONTH <= Month(DATEADD(WEEK, -26, GETDATE()))
)
group by WEEK, MONTH, YEAR
order by WEEK, YEAR
But I'm only getting week for the current year the previous year doesn't show.
I wonder if it's even possible to have the last year because without the pre-calculated tables I could get the last year with my querys but they want me to use this table.
Thank you for your help.
I don't see what your week value is but this will get you close:
SELECT SUM(CLOSED), WEEK, MONTH, YEAR
FROM E_GROUPE_INTERVENANT_SEMAINE_HISTORY
WHERE (--CURRENT YEAR WHEN YEAR IS NOT CROSSED
YEAR(DATEADD(WEEK, -18, GETDATE())) = YEAR(GETDATE())
AND
YEAR = YEAR(GETDATE())
AND
MONTH >= Month(DATEADD(WEEK, -18, GETDATE()))
)
OR
( --LAST MONTHS OF LAST YEAR WHEN YEAR IS SPANNED
MONTH >= Month(DATEADD(WEEK, -18, GETDATE()))
AND
YEAR = YEAR(DATEADD(WEEK, -18, GETDATE()))
)
OR
(--ALL MONTHS FOR CURRENT YEAR WHEN YEAR IS CROSSED
YEAR(DATEADD(WEEK, -18, GETDATE())) = YEAR(GETDATE()) - 1
AND
YEAR = YEAR(GETDATE())
AND
MONTH <= Month(DATEADD(WEEK, -18, GETDATE()))
)

Get date from week and year - date must be first day of the year - sql

I have a query that converts week and year to date.
But it returns the exact date.
But what i want is, I need the date to be such a day, that is same as the first day of the year.
dateadd (week, PromisedWeek-1, dateadd (year, PromisedYear-1900, 0)) - 4 -
datepart(dw, dateadd (week, PromisedWeek-1, dateadd (year, PromisedYear-1900, 0)) - 4) + 1
Hypothetical example.
My current query does is:
If week is 4 and year 2017, returns 26-Sun-2017
My need:
If week is 4 and year 2017 and January 1st was a Wednesday, its should return 29-Wednesday-2017.
Hoping that you guys get what I am trying to explain.
I need the query to return such a date which has the same day as that of the current year's 1st day.
Unless I'm reading this wrong, you're making things too hard on yourself. Just do this:
declare #year int; set #year = 2017;
declare #week int; set #week = 4;
select dateadd(week, #week, dateadd(year, #year - 1900, 0))
Result:
2017-01-29 00:00:00.000
The inner dateadd() gives you the first day of the requested year (Jan 1), regardless of weekday. If it's a Wednesday, you'll get a Wednesday. This year, it's Sunday. The outer dateadd() then adds full weeks to that, so you end up with the same day of the week, just like you asked.
If that's not what you want, please explain how it needs to be more complicated.
This is pretty easy if you have a calendar table. A calendar table is a table that contains every date and its parts for a given range. I created mine for 2010 to 2020 with this code:
declare #start_dt as date = '1/1/2010';
declare #end_dt as date = '1/1/2020';
declare #dates as table (
date_id date primary key,
date_year smallint,
date_month tinyint,
date_day tinyint,
weekday_id tinyint,
weekday_nm varchar(10),
month_nm varchar(10),
day_of_year smallint,
quarter_id tinyint,
first_day_of_month date,
last_day_of_month date,
start_dts datetime,
end_dts datetime
)
while #start_dt < #end_dt
begin
insert into #dates(
date_id, date_year, date_month, date_day,
weekday_id, weekday_nm, month_nm, day_of_year, quarter_id,
first_day_of_month, last_day_of_month,
start_dts, end_dts
)
values(
#start_dt, year(#start_dt), month(#start_dt), day(#start_dt),
datepart(weekday, #start_dt), datename(weekday, #start_dt), datename(month, #start_dt), datepart(dayofyear, #start_dt), datepart(quarter, #start_dt),
dateadd(day,-(day(#start_dt)-1),#start_dt), dateadd(day,-(day(dateadd(month,1,#start_dt))),dateadd(month,1,#start_dt)),
cast(#start_dt as datetime), dateadd(second,-1,cast(dateadd(day, 1, #start_dt) as datetime))
)
set #start_dt = dateadd(day, 1, #start_dt)
end
select *
into Calendar
from #dates
Once you have a calendar table you can query for the correct weekday in the PromisedWeek
declare #PromisedYear as numeric
declare #PromisedWeek as int
set #PromisedYear = 2017
set #PromisedWeek = 4
select concat(date_day, '-', weekday_nm, '-', #PromisedYear)
from Calendar as c
where
weekday_id =
(select weekday_ID
from Calendar
where date_year = #PromisedYear
and day_of_year = 1
)
and datepart(week, date_id) = #PromisedWeek
and date_year = #PromisedYear
Turns out that January 1st 2017 was a Sunday and the 4th Sunday in 2017 was January 22nd, so the return is 22-Sunday-2017

Creating a date from Week of month and Day of week in SQL server

I have to get/create date from the user input of week of month (week number in that month - 1st,2nd,3rd,4th and last) and day of week (sunday,monday..) in SQL server.
Examples:
4th Sunday of every month, Last Friday of every month, First Monday etc.
I was able to do it easily in .net but SQL server does seem limited in the date functions.
I am having to use lot of logic to get the date. To calculate the date using the above two parameters I had to use lot of datepart function.
Any suggestions on how to come up with the optimal SQL query for such a function?
I created a function other day for another OP GET Month, Quarter based on Work Week number
This function takes the current year as default it can be further modified to take Year as a parameter too.
an extension to that function can produce the results you are looking for ....
WITH X AS
(
SELECT TOP (CASE WHEN YEAR(GETDATE()) % 4 = 0 THEN 366 ELSE 365 END)-- to handle leap year
DATEADD(DAY
,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1
, CAST(YEAR(GETDATE()) AS VARCHAR(4)) + '0101' )
DayNumber
From master..spt_values
),DatesData AS(
SELECT DayNumber [Date]
,DATEPART(WEEKDAY,DayNumber) DayOfTheWeek
,DATEDIFF(WEEK,
DATEADD(WEEK,
DATEDIFF(WEEK, 0, DATEADD(MONTH,
DATEDIFF(MONTH, 0, DayNumber), 0)), 0)
, DayNumber- 1) + 1 WeekOfTheMonth
FROM X )
SELECT * FROM DatesData
WHERE DayOfTheWeek = 6 -- A function would expect these two parameters
AND WeekOfTheMonth = 4 -- #DayOfTheWeek and #WeekOfTheMonth
Here is a general formula:
declare #month as datetime --set to the first day of the month you wish to use
declare #week as int --1st, 2nd, 3rd...
declare #day as int --Day of the week (1=sunday, 2=monday...)
--Second monday in August 2015
set #month = '8/1/2015'
set #week = 2
set #day = 2
select dateadd(
day,
((7+#day) - datepart(weekday, #month)) % 7 + 7 * (#week-1),
#month
)
You can also find the last, 2nd to last... etc with this reverse formula:
--Second to last monday in August 2015
set #month = '8/1/2015'
set #week = 2
set #day = 2
select
dateadd(
day,
-((7+datepart(weekday, dateadd(month,1,#month)-1)-#day)) % 7 - 7 * (#week-1),
dateadd(month,1,#month)-1
)

SQL Server : get day of month from Year Month WeekOfMonth

I have a complicated calendar report coming from a SQL query where I have the year, month and WeekNumOfMonth (meaning the week number from the beginning of the month).
I need a function to return the DayOfMonth (ie: values of between 1-28,29,30 or 31 depending on the month) given Year, month and WeekNumOfMonth.
I have seen functions that do this given WeekNum from beginning of year but I only have WeekNum of the given month. I just can't seem to give the datePart and dateAdd functions right. Any help would be appreciated.
Note : Combination of Year ,Month ,Week Number will not give you the
day name because a week contain more than one day so how can you find
which day is it ? it is not possible
example : year 2015 month 06 week number 1
then it has a combination of
1 monday
2 tuesday
3 wednesday
4 thursday
5 friday
6 saturday
then with these results how can you predit the exact day ??
Else need some conditions or restrictions
If you have Year, Month , and date then you can find the day name using the below query it will give the start and end date of the week then you can take the dates from start date and end with end date
SELECT (
DATENAME(dw,
CAST(DATEPART(m, GETDATE()) AS VARCHAR)
+ '/'
+ CAST(DATEPART(d, '2015-06-23') AS VARCHAR)
+ '/'
+ CAST(DATEPART(yy, getdate()) AS VARCHAR))
)
Update
If you need all days between the current week then use the following query
DECLARE
#Year INT,
#Week INT,
#FirstDayOfYear DATETIME,
#FirstMondayOfYear DATETIME,
#StartDate DATETIME,
#EndDate DATETIME
SET #Year = 2015
SET #Week = 24// week number is based on year and it is not based on month
-- Get the first day of the provided year.
SET #FirstDayOfYear = CAST('1/1/' + CAST(#YEAR AS VARCHAR) AS DATETIME)
-- Get the first monday of the year, then add the number of weeks.
SET #FirstMondayOfYear = DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, #FirstDayOfYear), #FirstDayOfYear)), 0)
SET #StartDate = DATEADD(WEEK, #Week - 1, #FirstMondayOfYear)
-- Set the end date to one week past the start date.
SET #EndDate = DATEADD(WEEK, 1, #StartDate)
SELECT #StartDate AS StartDate, DATEADD(SECOND, -1, #EndDate) AS EndDate
Update
if do not have the year week number then use the below query
DATEPART(WEEK,'2015-06-01')
The above query is one example
In the query you have two values that is Year and Month
you need only the third value that means date
as a technique you can give 01 as the date then it will give the
starting week number your selected month
here it will result 23 so you got the idea in 2015 - month 06 starts with week number 23 so then you can easily do your stuff
Idea : if your week number is 1 then take 23 as week if it is 2 then 24 like wise..

Labor Day Vs. Thanksgiving

I am creating a calendar table for my warehouse. I will use this as a foreign key for all the date fields.
The code shown below creates the table and populates it. I was able to figure out how to find Memorial Day (last Monday of May) and Labor Day (first Monday of September).
SET NOCOUNT ON
DROP Table dbo.Calendar
GO
Create Table dbo.Calendar
(
CalendarId Integer NOT NULL,
DateValue Date NOT NULL,
DayNumberOfWeek Integer NOT NULL,
NameOfDay VarChar (10) NOT NULL,
NameOfMonth VarChar (10) NOT NULL,
WeekOfYear Integer NOT NULL,
JulianDay Integer NOT NULL,
USAIsBankHoliday Bit NOT NULL,
USADayName VarChar (100) NULL,
)
ALTER TABLE dbo.Calendar ADD CONSTRAINT
DF_Calendar_USAIsBankHoliday DEFAULT 0 FOR USAIsBankHoliday
GO
ALTER TABLE dbo.Calendar ADD CONSTRAINT
DF_Calendar_USADayName DEFAULT '' FOR USADayName
GO
Declare #StartDate DateTime = '01/01/2000'
Declare #EndDate DateTime = '01/01/2020'
While #StartDate < #EndDate
Begin
INSERT INTO dbo.Calendar
(
CalendarId,
DateValue,
WeekOfYear,
DayNumberOfWeek,
NameOfDay,
NameOfMonth,
JulianDay
)
Values
(
YEAR (#StartDate) * 10000 + MONTH (#StartDate) * 100 + Day (#StartDate), --CalendarId
#StartDate, -- DateValue
DATEPART (ww, #StartDate), -- WeekOfYear
DATEPART (dw, #StartDate), -- DayNumberOfWeek
DATENAME (dw, #StartDate), -- NameOfDay
DATENAME (M, #StartDate), -- NameOfMonth
DATEPART (dy, #StartDate) -- JulianDay
)
Set #StartDate += 1
End
--=========================== Weekends
-- saturday and sunday
UPDATE dbo.Calendar SET USAIsBankHoliday = 1, USADayName += 'Weekend, ' WHERE DayNumberOfWeek IN (1, 7)
--=========================== Bank Holidays
-- new years day
UPDATE dbo.Calendar SET USAIsBankHoliday = 1, USADayName += 'New Year''s Day, ' WHERE (CalendarId % 2000) IN (101)
-- memorial day (last Monday in May)
UPDATE dbo.Calendar
SET USAIsBankHoliday = 1,
USADayName += 'Memorial Day, '
WHERE 1=1
AND CalendarId IN
(
SELECT MAX (CalendarId)
FROM dbo.Calendar
WHERE MONTH (DateValue) = 5
AND DATEPART (DW, DateValue)=2
GROUP BY YEAR (datevalue)
)
-- independence day
UPDATE dbo.Calendar SET USAIsBankHoliday = 1, USADayName += 'Independence Day, ' WHERE (CalendarId % 2000) IN (704)
-- labor day (first Monday in September)
UPDATE dbo.Calendar
SET USAIsBankHoliday = 1,
USADayName += 'Labor Day, '
WHERE 1=1
AND CalendarId IN
(
SELECT MIN (CalendarId)
FROM dbo.Calendar
WHERE MONTH (DateValue) = 9
AND DATEPART (DW, DateValue)=2
GROUP BY YEAR (datevalue)
)
-- thanksgiving day (fourth Thursday in November)
UPDATE dbo.Calendar
SET USAIsBankHoliday = 1,
USADayName += 'Thanksgiving Day, '
WHERE 1=1
AND CalendarId IN
(
SELECT Max (CalendarId)
FROM dbo.Calendar
WHERE MONTH (DateValue) = 11
AND DATEPART (DW, DateValue)=5
GROUP BY YEAR (datevalue)
)
-- christmas
UPDATE dbo.Calendar SET USAIsBankHoliday = 1, USADayName += 'Christmas Day, ' WHERE (CalendarId % 2000) IN (1225)
--=========================== Other named days
-- new years eve
UPDATE dbo.Calendar SET USADayName += 'New Year''s Eve, ' WHERE (CalendarId % 2000) IN (1231)
-- christmas eve
UPDATE dbo.Calendar SET USADayName += 'Christmas Eve, ' WHERE (CalendarId % 2000) IN (1224)
-- boxing day
UPDATE dbo.Calendar SET USADayName += 'Boxing Day, ' WHERE (CalendarId % 2000) IN (1226)
--=========================== Remove trailing comma
UPDATE dbo.Calendar SET USADayName = SubString (USADayName, 1, LEN (USADayName) -1) WHERE LEN (USADayName) > 2
SELECT * FROM dbo.Calendar
I am stumped on figuring out Thanksgiving day (Thursday of the last FULL week of November).
Edit:
Correction based on comment by John Sauer
Thanksgiving is the fourth Thursday of November. However, upon checking several years, I find that it has turned out to also be the Thursday of the last full week of Nov.
I am stumped on figuring out Thanksgiving day (Thursday of the last FULL week of November).
Last Saturday of November - 2
Take the last Saturday of November, and subtract two days ;)
WITH cal AS
(
SELECT CAST('2009-30-11' AS DATETIME) AS cdate
UNION ALL
SELECT DATEADD(day, -1, cdate)
FROM cal
WHERE cdate >= '2009-01-11'
)
SELECT TOP 1 DATEADD(day, -2, cdate)
FROM cal
WHERE DATEPART(weekday, cdate) = 6
For the complex algorithms, it's sometimes better to find a matching date from a set than trying to construct an enormous single-value formula.
See this article in my blog for more detail:
Checking event dates
declare #thedate datetime
set #thedate = '11/24/2011'
select 1
where datepart(dw, #thedate) = 5 and datepart(m, #thedate) = 11 AND (datepart(dd, #thedate) - 21) between 0 and 6
Is the date a Thursday in November and is there less than a week remaining.
We use a neat Scalar-valued Function in our database that we can call to determine whether or not a date is a NonWorkDay. We manually add special dates for Observed Holidays.
Create Function:
CREATE FUNCTION [dbo].[NonWorkDay](#date as DATETIME)
RETURNS bit
AS
BEGIN
if #date is not null
begin
-- Weekends
if DATEPART(weekday, #date)=1 return 1 --Sunday
if DATEPART(weekday, #date)=7 return 1 --Saturday
-- JAN
if month(#date)=1 AND day(#date)=1 return 1 -- New Years Day
-- MAY
if month(#date)=5 and DATEPART(weekday, #date)=2 and day(#date)>24 and day(#date)<=31 return 1 --Memorial Day
-- JULY
if month(#date)=7 AND day(#date)=4 return 1 -- July 4th
-- SEP
if month(#date)=9 and DATEPART(weekday, #date)=2 and day(#date)<=7 return 1--Labor Day
-- NOV
if month(#date)=11 and DATEPART(weekday, #date)=5 and day(#date)>21 and day(#date)<=28 return 1--Thanksgiving
if month(#date)=11 and DATEPART(weekday, #date)=6 and day(#date)>22 and day(#date)<=29 return 1 -- Black Friday
-- DEC
if month(#date)=12 AND day(#date)=24 return 1 -- Christmas Eve
if month(#date)=12 AND day(#date)=25 return 1 -- Christmas
if month(#date)=12 AND day(#date)=31 return 1 -- NYE
-- Office Closed for Observed Holiday Dates
if month(#date)=7 AND day(#date)=5 AND year(#date)=2021 return 1 -- 4th of July Observed for 2021
if month(#date)=12 AND day(#date)=26 AND year(#date)=2022 return 1 -- Christmas Day observed for 2022
if month(#date)=1 AND day(#date)=2 AND year(#date)=2023 return 1 -- New Years Day observed for 2023
if month(#date)=7 AND day(#date)=3 AND year(#date)=2026 return 1 -- 4th of July Observed for 2026
if month(#date)=7 AND day(#date)=5 AND year(#date)=2027 return 1 -- 4th of July Observed for 2027
end
return 0
END
GO
Call Function:
IF [dbo].[NonWorkDay](Getdate())= 0 -- If (not a NonWorkDay -ie not Sat or Sun)

Resources