NetWorkDays Function in SQL SERVER 2012 - sql-server

Is there a function in sql server 2012 which calculates only working days?
I have been searching but with no luck so far.
Thanks!

No, SQL Server doesn't have such functions, but you can use calendar table:
DECLARE #date_start date = '2016-01-01',
#date_end date = '2016-12-31';
WITH cte as (
SELECT #date_start as [d], 0 as Level
UNION ALL
SELECT DATEADD(day,1,[d]), [level] + 1 as [level]
from cte
WHERE [level] < DATEDIFF(day,#date_start,#date_end)
),
holidays as ( --table with holidays (USA)
SELECT * FROM (VALUES
('2016-01-01'),
('2016-01-18'),
('2016-02-15'),
('2016-05-30'),
('2016-07-04'),
('2016-09-05'),
('2016-10-10'),
('2016-11-11'),
('2016-11-24'),
('2016-12-26')) as t(d)
)
SELECT c.d,
CASE WHEN DATEPART(WEEKDAY,c.d) IN (1,7) THEN 0 --Saturday and Sunday, use (6,7) for Friday,Saturday
WHEN h.d IS NOT NULL THEN 0
ELSE 1 END as isWorking
FROM cte c
LEFT JOIN holidays h
ON c.d=h.d
OPTION (MAXRECURSION 1000);
It will generate a table with all dates in 2016 year and flag - is the day working or not.

Below is the high level overview of how you can do this..
Create a dummy table which holds dates in this format...
date isholiday
20160101 1
20160102 0
Now from your main table which holds employees attendance ,join above table like..
select empid,sum(Case when mt.datee is not null then 1 else 0 end) as workingdays
from
dummydatees dt
left join
maintable mt
on dt.datee=mt.datee
where dt.isholiday=0

This script will calculate the total working days excluding Saturday, Sunday and holidays. I have to list all the holidays since I don't have a table for holidays. You can modify it so that it will meet your requirements.
DECLARE #MyCounter int = 0, #TempDate datetime, #EndDate datetime;
SET #TempDate = DATEADD(d,1,'2017-5-27')
SET #EndDate = '2017-6-3'
WHILE #TempDate <= #EndDate
BEGIN
IF DATENAME(DW,#TempDate) = 'Sunday' OR DATENAME(DW,#TempDate) = 'Saturday'
SET #MyCounter = #MyCounter
ELSE IF #TempDate not in ('2017-1-1', '2017-1-16', '2017-2-20', '2017-5-29', '2017-7-4', '2017-9-4', '2017-10-9', '2017-11-11', '2017-12-25')
SET #MyCounter = #MyCounter + 1
SET #TempDate = DATEADD(d,1,#TempDate)
CONTINUE
END
PRINT #MyCounter
PRINT #TempDate

Related

Display last business day of every month in the database table. (Business day does not include Saturday, Sunday)

I want to display last business day (excludes Saturday, Sunday) of every month in the database table.
We use pgadmin, I'm guessing it's similar to SQL Server, here is a query we use:
bolReturn = true;
--Get the day of week
SELECT EXTRACT(DOW FROM _start)
Into dayofweek;
--Sataurday
if dayofweek = 6 then
bolReturn = false;
--Sunday
ELSIF dayofweek = 0 then
bolReturn = false;
end if;
--Check against office closing
select count(*) as start
into intCount
from tables.officeclosed where closeddate = _start;
if intCount > 0 then
bolReturn = false;
end if;
return
tables.officeclosed would contain days you know you have off such as holidays and _start is the date you are passing in.
Try this:
create function dbo.LastBusinessDayOfMonth ( #Date date )
returns date as
begin
declare #Result date;
-- Find last day of the month
set #Result = EOMONTH(#Date);
-- If this date is Saturday or Sunday,
-- choose the preceding date
while DATEDIFF(day,'0001-01-01',#Result)%7 >= 5
set #Result = DATEADD(day,-1,#Result)
return #Result;
end
Certainly need more info, and as mentioned by Sean, you need to consider public holidays etc.
Following uses cross apply with "TOP 1" from subquery selecting 3 dates.
SET DATEFIRST 1
select *
from
(
SELECT Months.Idx, EOMONTH(datefromparts(year(getdate()), Months.idx, 1)) as EOMDate
FROM ( VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12) ) Months(Idx)
) Months
cross apply (
select top 1 mldx.date AS LastBusinessDay
from
(
select Months.EOMDate date
union all select dateadd(day, -1, Months.EOMDate )
union all select dateadd(day, -2, Months.EOMDate )
) mldx
where datepart(weekday, mldx.date ) <= 5
order by mldx.date desc
) lastBusinessDay

SQL Server : Recursive function to get last working day

I have a list holidays stored in BankHolidays table for a couple of years. Now i need to compute the last working day for any given date.
CTE may help in this scenario, however i prefer to have this snippet as a function.
I have written the following (pseudo) code to get my result, but i'm not able to do a recursive call
CREATE FUNCTION dbo.Get_Previous_Working_Day(#Day date) RETURNS date
AS BEGIN
if(datename(dw,#Day) = 'Sunday')
set #Day = DATEADD(day, -1, #Day)
if(datename(dw,#Day) = 'Saturday')
set #Day = DATEADD(day, -1, #Day)
if not exists (select count(1) from BankHolidays where datepart(yyyy,HolidayDate) = datepart(yyyy,#Day))
return null
else
begin
if exists (select count(1) from BankHolidays where convert(date,HolidayDate) = convert(date,#Day))
begin
set #Day = DATEADD(day, -1, #Day)
dbo.Get_Previous_Working_Day(#Day) --This recurise call may need to be modified
end
else
return #Day
end
end
Thank you in advance
Edit 1
Error : Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32).
I guess this is due to the stack over flow on recursive calls and its not able to decide when to quit. Seems like a logical error. Unfortunately i couldn't figure where its going wrong
BankHolidays Table:
--------------------------------------------------
HolidayDate DayofWeek Description
--------------------------------------------------
2015-01-01 Thursday New year
2010-01-01 Friday New year
2015-04-03 Friday Good Friday
2015-05-04 Monday Early May bank holiday
2014-06-11 Wednesday June 14 - NEW ENTRY
2015-05-25 Monday Spring bank holiday
2015-12-28 Monday Boxing Day (substitute day)
2015-04-06 Monday Easter Monday
2015-08-31 Monday Summer bank holiday
2015-12-25 Friday Christmas Day
Expected Output
Get_Previous_Working_Day('2015-01-01') -- Result : 2014-12-31
Get_Previous_Working_Day('2015-01-02') -- Result : 2014-12-31
Get_Previous_Working_Day('2010-01-04') -- Result : 2009-12-31
Get_Previous_Working_Day('2015-04-06') -- Result : 2015-04-02
Get_Previous_Working_Day('2015-12-05') -- Result : 2015-12-04
Get_Previous_Working_Day('2015-12-06') -- Result : 2015-04-04
Get_Previous_Working_Day('2014-06-12') -- Result : 2014-06-10
CREATE TABLE dbo.BankHolidays
(
HolidayDate DATE PRIMARY KEY,
[DayofWeek] AS DATENAME(dw, HolidayDate),
[Description] VARCHAR(100)
)
INSERT INTO dbo.BankHolidays (HolidayDate, [Description])
VALUES
('2015-01-01', 'New year'),
('2010-01-01', 'New year'),
('2015-04-03', 'Good Friday'),
('2015-05-04', 'Early May bank holiday'),
('2014-06-11', 'June 14 - NEW ENTRY'),
('2015-05-25', 'Spring bank holiday'),
('2015-12-28', 'Boxing Day (substitute day)'),
('2015-04-06', 'Easter Monday'),
('2015-08-31', 'Summer bank holiday'),
('2015-12-25', 'Christmas Day')
GO
CREATE FUNCTION dbo.Get_Previous_Working_Day
(
#date DATE
)
RETURNS DATE
AS BEGIN
DECLARE #result DATE
;WITH cte AS
(
SELECT dt = DATEADD(DAY, -1, #date)
UNION ALL
SELECT DATEADD(DAY, -1, dt)
FROM cte
WHERE dt > DATEADD(DAY, -30, #date)
)
SELECT TOP(1) #result = dt
FROM cte
WHERE dt NOT IN (SELECT t.HolidayDate FROM dbo.BankHolidays t)
AND DATENAME(dw, dt) NOT IN ('Saturday', 'Sunday')
ORDER BY dt DESC
OPTION (MAXRECURSION 0)
RETURN #result
END
GO
SELECT dbo.Get_Previous_Working_Day('2015-12-29')
#Ramu,
Please find the corrected part here.
There are two issues in the function definition. One is in the calling of function. Since the function should be called by using SELECT statement so you have to use that.
Second, the function should end by return statement.
So the correct definition would be:
CREATE FUNCTION dbo.Get_Previous_Working_Day(#Day date) RETURNS date
AS BEGIN
if(datename(dw,#Day) = 'Sunday')
set #Day = DATEADD(day, -1, #Day)
if(datename(dw,#Day) = 'Saturday')
set #Day = DATEADD(day, -1, #Day)
if not exists (select count(1) from BankHolidays where datepart(yyyy,HolidayDate) = datepart(yyyy,#Day))
return null
else
begin
if exists (select count(1) from BankHolidays where convert(date,HolidayDate) = convert(date,#Day))
begin
set #Day = DATEADD(day, -1, #Day)
select #Day = dbo.Get_Previous_Working_Day(#Day) --This recurise call may need to be modified
return #Day
end
else
return #Day
end
return #Day
end

Need a function to return date of next/Previous business day SQL Server

I need help creating a sql server (2012) function that when given the following variables
Monday True/False
Tuesday True/False
Wednesday True/False
Thursday True/False
Friday True/False
Saturday True/False
Sunday True/False
Date1 mm/dd/yyyy
Type Next/Previous
it will return the date of the next (or previous) business day for Date1
So for example if Date1 is 12/22/2014 (Monday), and passing the parameters below, it would return 12/26/2014 (Friday)
Monday True
Tuesday False
Wednesday False
Thursday False
Friday True
Saturday True
Sunday True
Date1 12/22/2014
Type Next
The following query calculates the next valid "day" after any given day of the week. And it calculates the number of days to that day.
with days as (
select 1 as dow, 'Monday' as name, #Monday as flag union all
select 2, 'Tuesday', #Tuesday union all
select 3, 'Wednesday', #Wednesday union all
select 4, 'Thursday', #Thursday union all
select 5, 'Friday', #Friday union all
select 6, 'Saturday', #Saturday union all
select 7, 'Sunday', #Sunday
)
select d.*, d2.dow as next_dow,
(case when d2.dow > d.dow then d2.dow - d.dow else d2.dow - d.dow + 7 end) as days_to_next
from days d cross apply
(select top 1 d2.dow
from days d2
where d2.flag = 'true'
order by (case when d2.dow > d.dow then 1 else 2 end), d2.dow
) d2;
The next step is just to lookup the day you want:
with days as (
select 1 as dow, 'Monday' as name, #Monday as flag union all
select 2, 'Tuesday', #Tuesday union all
select 3, 'Wednesday', #Wednesday union all
select 4, 'Thursday', #Thursday union all
select 5, 'Friday', #Friday union all
select 6, 'Saturday', #Saturday union all
select 7, 'Sunday', #Sunday
)
select dateadd(day,
(case when d2.dow > d.dow then d2.dow - d.dow else d2.dow - d.dow + 7 end),
#Date1
)
from days d cross apply
(select top 1 d2.dow
from days d2
where d2.flag = 'true'
order by (case when d2.dow > d.dow then 1 else 2 end), d2.dow
) d2
where d.dow = datename(weekday, #Date1);
Of course, datename() could return non-English names if with non-English internationalization settings. The query could be adjusted if that logic doesn't work.
You have to create a table type variable first:
CREATE TYPE BusinessDateTableType AS TABLE
(
[WeekDay] VARCHAR(50),
IsBusinessDate BIT
);
Then create the function that takes a table-valued parameter of the above type:
CREATE FUNCTION UDF_GetNextBusinessDay
(
#businessDates BusinessDateTableType READONLY,
#type VARCHAR(10),
#day DATE
)
RETURNS DATE
AS
BEGIN
-- Declare the return variable here
DECLARE #nextBusinessDate DATE
;WITH cte AS (
SELECT CASE
WHEN #type = 'Next' THEN 1
WHEN #type = 'Previous' THEN -1
END AS i
UNION ALL
SELECT CASE
WHEN #type = 'Next' THEN i + 1
WHEN #type = 'Previous' THEN i -1
END AS i
FROM cte
WHERE ABS(i) < 7
)
SELECT TOP 1 #nextBusinessDate = DATEADD(day, i, #day)
FROM cte AS d1
INNER JOIN #businessDates AS d2 ON DATENAME(DW, DATEADD(day, i, #day)) = d2.WeekDay
WHERE d2.IsBusinessDate = 1
ORDER BY ABS(i)
-- Return the result of the function
RETURN #nextBusinessDate
END
EDIT:
We can easily substitute the table-type variable in the UDF with seven BIT type variables, then use a table variable inside the UDF and populate it with the values of these variables:
CREATE FUNCTION UDF_GetNextBusinessDay2
(
#IsMonWorkingDay BIT,
#IsTueWorkingDay BIT,
#IsWedWorkingDay BIT,
#IsThuWorkingDay BIT,
#IsFriWorkingDay BIT,
#IsSatWorkingDay BIT,
#IsSunWorkingDay BIT,
#type VARCHAR(10),
#day DATE
)
RETURNS DATE
AS
BEGIN
-- Declare the return variable here
DECLARE #nextBusinessDate DATE
DECLARE #businessDates TABLE ([WeekDay] VARCHAR(50), IsBusinessDate BIT)
INSERT INTO #businessDates VALUES
('Monday', #IsMonWorkingDay),
('Tuesday', #IsTueWorkingDay),
('Wednesday', #IsWedWorkingDay),
('Thursday', #IsThuWorkingDay),
('Friday', #IsFriWorkingDay),
('Saturday', #IsSatWorkingDay),
('Sunday', #IsSunWorkingDay)
;WITH cte AS (
SELECT CASE
WHEN #type = 'Next' THEN 1
WHEN #type = 'Previous' THEN -1
END AS i
UNION ALL
SELECT CASE
WHEN #type = 'Next' THEN i + 1
WHEN #type = 'Previous' THEN i -1
END AS i
FROM cte
WHERE ABS(i) < 7
)
SELECT TOP 1 #nextBusinessDate = DATEADD(day, i, #day)
FROM cte AS d1
INNER JOIN #businessDates AS d2 ON DATENAME(DW, DATEADD(day, i, #day)) = d2.WeekDay
WHERE d2.IsBusinessDate = 1
ORDER BY ABS(i)
-- Return the result of the function
RETURN #nextBusinessDate
END
Using the second version of the UDF with this test data:
DECLARE #type VARCHAR(10) = 'Next'
DECLARE #day DATE = '2014-12-22'
DECLARE #nextBusinessDate DATE
SET #nextBusinessDate = dbo.UDF_GetNextBusinessDay2(1,0,0,0,0,0,1, #type, #day)
SELECT #nextBusinessDate
produces the following result:
2014-12-28
This code gets the next or previous business day by creating a list of three dates starting from the start date. If we want the previous business day then we get the biggest date if we want the next business day we get the smallest date.
DECLARE #direction AS INT =1
--If direction is 1 you get the next business day.
--If direction is -1 you get the previous business day.
DECLARE #startDate AS DATE ='2017-07-21'
SELECT CASE #direction WHEN 1 THEN MIN(listOfDays.d) ELSE MAX(listOfDays.d) END AS nextBusinessDay FROM(
(SELECT DATEADD(DAY,Number*#direction,#startDate)AS d FROM (VALUES (1),(2),(3)) AS Numbers(Number) )) listOfDays
WHERE DATEPART(WEEKDAY,listOfDays.d) NOT IN (1,7)
The following code selects from a list of days the next business day excluding weekends and non working dates.
REQUIREMENTS: In order for the following code to work a Numbers table must exist as well as a Non_working_dates table.
The Numbers table should have at least one field. The field name must be Number. The field must not accept duplicates. The data type must be INT.
The Non_working_days table must have as least one field. The field name must be Non_working_date. The field must accept no duplicate values. The data type must be DATE without time.
DECLARE #startDate AS DATE = '2017-07-03'
--Step 2. Get the next business day.
SELECT TOP 1 listOfDates.d AS nextBusinessDay FROM
(
--Step 1. Get a month's worth of dates.
The dates must not be weekend days or non working days.
SELECT DATEADD(DAY,Number,#startDate)AS d FROM Numbers WHERE Number < 30
) listOfDates
WHERE listOfDates.d> #startDate AND DATEPART(WEEKDAY,listofdates.d) NOT IN (1,7)
AND listOfDates.d NOT IN (SELECT Non_working_date FROM Non_working_dates WHERE Non_working_date>=#startDate)
ORDER BY listOfDates.d ASC

Set based solution for processing rows in a SQL table

Can someone steer me in the right direction for solving this issue with a set-based solution versus cursor-based?
Given a table with the following rows:
Date Value
2013-11-01 12
2013-11-12 15
2013-11-21 13
2013-12-01 0
I need a query that will give me a row for each date between 2013-11-1 and 2013-12-1, as follows:
2013-11-01 12
2013-11-02 12
2013-11-03 12
...
2013-11-12 15
2013-11-13 15
2013-11-14 15
...
2013-11-21 13
2013-11-21 13
...
2013-11-30 13
2013-11-31 13
Any advice and/or direction will be appreciated.
The first thing that came to my mind was to fill in the missing dates by looking at the day of the year. You can do this by joining to the spt_values table in the master DB and adding the number to the first day of the year.
DECLARE #Table AS TABLE(ADate Date, ANumber Int);
INSERT INTO #Table
VALUES
('2013-11-01',12),
('2013-11-12',15),
('2013-11-21',13),
('2013-12-01',0);
SELECT
DateAdd(D, v.number, MinDate) Date
FROM (SELECT number FROM master.dbo.spt_values WHERE name IS NULL) v
INNER JOIN (
SELECT
Min(ADate) MinDate
,DateDiff(D, Min(ADate), Max(ADate)) DaysInSpan
,Year(Min(ADate)) StartYear
FROM #Table
) dates ON v.number BETWEEN 0 AND DaysInSpan - 1
Next I would wrap that to make a derived table, and add a subquery to get the most recent number. Your end result may look something like:
DECLARE #Table AS TABLE(ADate Date, ANumber Int);
INSERT INTO #Table
VALUES
('2013-11-01',12),
('2013-11-12',15),
('2013-11-21',13),
('2013-12-01',0);
-- Uncomment the following line to see how it behaves when the date range spans a year end
--UPDATE #Table SET ADate = DateAdd(d, 45, ADate)
SELECT
AllDates.Date
,(SELECT TOP 1 ANumber FROM #Table t WHERE t.ADate <= AllDates.Date ORDER BY ADate DESC)
FROM (
SELECT
DateAdd(D, v.number, MinDate) Date
FROM
(SELECT number FROM master.dbo.spt_values WHERE name IS NULL) v
INNER JOIN (
SELECT
Min(ADate) MinDate
,DateDiff(D, Min(ADate), Max(ADate)) DaysInSpan
,Year(Min(ADate)) StartYear
FROM #Table
) dates ON v.number BETWEEN 0 AND DaysInSpan - 1
) AllDates
Another solution, not sure how it compares to the two already posted performance wise but it's a bit more concise:
Uses a numbers table:
Linky
Query:
DECLARE #SDATE DATETIME
DECLARE #EDATE DATETIME
DECLARE #DAYS INT
SET #SDATE = '2013-11-01'
SET #EDATE = '2013-11-29'
SET #DAYS = DATEDIFF(DAY,#SDATE, #EDATE)
SELECT Num, DATEADD(DAY,N.Num,#SDATE), SUB.[Value]
FROM Numbers N
LEFT JOIN MyTable M ON DATEADD(DAY,N.Num,#SDATE) = M.[Date]
CROSS APPLY (SELECT TOP 1 [Value]
FROM MyTable M2
WHERE [Date] <= DATEADD(DAY,N.Num,#SDATE)
ORDER BY [Date] DESC) SUB
WHERE N.Num <= #DAYS
--
SQL Fiddle
It's possible, but neither pretty nor very performant at scale:
In addition to your_table, you'll need to create a second table/view dates containing every date you'd ever like to appear in the output of this query. For your example it would need to contain at least 2013-11-01 through 2013-12-01.
SELECT m.date, y.value
FROM your_table y
INNER JOIN (
SELECT md.date, MAX(my.date) AS max_date
FROM dates md
INNER JOIN your_table my ON md.date >= my.date
GROUP BY md.date
) m
ON y.date = m.max_date

How do I take a day of the year and 'bucket it' into weeks of the year in Microsoft SQL? Used in manufacturing scenarios for material requirements

I have a need to create a gross requirements report that takes how much supply and demand of a item in inventory from a start date onwards and 'buckets' it into different weeks of the year so that material planners know when they will need a item and if they have enough stock in inventory at that time.
As an example, today’s date (report date) is 8/27/08. The first step is to find the date for the Monday of the week the report date falls in. In this case, Monday would be 8/25/08. This becomes the first day of the first bucket. All transactions that fall before that are assigned to week #0 and will be summarized as the beginning balance for the report. The remaining buckets are calculated from that point. For the eighth bucket, there is no ending date so any transactions after that 8th bucket start date are considered week #8.
WEEK# START DATE END DATE
0.......None..........8/24/08
1.......8/25/08.......8/31/08
2.......9/1/08.........9/7/08
3.......9/8/08.........9/14/08
4.......9/15/08.......9/21/08
5.......9/22/08.......9/28/08
6.......9/29/08.......10/5/08
7.......10/06/08.....10/12/08
8.......10/13/08......None
How do I get the week #, start date, end date for a given date?
I've always found it easiest and most efficient (for SQL Server) to construct a table with one row for every week into the future through your domain horizon; and join to that (with a "WHERE GETDATE() >= MONDATE AND NOT EXISTS (SELECT 1 FROM table WHERE MONDATE < GETDATE())".
Anything you try to do with UDF's will be much less efficient and I find more difficult to use.
You can get Monday for any given date in a week as:
DATEADD(d, 1 - DATEPART(dw, #date), #date)
and you can write a stored procedure with the following body
-- find Monday at that week
DECLARE #currentDate SMALLDATETIME
SELECT #currentDate = DATEADD(d, 1 - DATEPART(dw, #date), #date)
-- create a table and insert the first record
DECLARE #weekTable TABLE (Id INT, StartDate SMALLDATETIME, EndDate SMALLDATETIME)
INSERT INTO #weekTable VALUES (0, NULL, #currentDate)
-- increment the date
SELECT #currentDate = DATEADD(d, 1, #currentDate)
-- iterate for 7 more weeks
DECLARE #id INT
SET #id = 1
WHILE #id < 8
BEGIN
INSERT INTO #weekTable VALUES (#id, #currentDate, DATEADD(d, 6, #currentDate))
SELECT #currentDate = DATEADD(ww, 1, #currentDate)
SET #id = #id + 1
END
-- add the last record
INSERT INTO #weekTable VALUES (8, #currentDate, NULL)
-- select the values
SELECT Id 'Week #', StartDate 'Start Date', EndDate 'End Date'
FROM #weekTable
When I pass
#date = '20080827'
to this procedure, I get the following
Week # Start Date End Date
0 NULL 2008-08-24 00:00:00
1 2008-08-25 00:00:00 2008-08-31 00:00:00
2 2008-09-01 00:00:00 2008-09-07 00:00:00
3 2008-09-08 00:00:00 2008-09-14 00:00:00
4 2008-09-15 00:00:00 2008-09-21 00:00:00
5 2008-09-22 00:00:00 2008-09-28 00:00:00
6 2008-09-29 00:00:00 2008-10-05 00:00:00
7 2008-10-06 00:00:00 2008-10-12 00:00:00
8 2008-10-13 00:00:00 NULL
--SQL sets the first day of the week as sunday and for our purposes we want it to be Monday.
--This command does that.
SET DATEFIRST 1
DECLARE
#ReportDate DATETIME,
#Weekday INTEGER,
#NumDaysToMonday INTEGER,
#MondayStartPoint DATETIME,
#MondayStartPointWeek INTEGER,
#DateToProcess DATETIME,
#DateToProcessWeek INTEGER,
#Bucket VARCHAR(50),
#DaysDifference INTEGER,
#BucketNumber INTEGER,
#NumDaysToMondayOfDateToProcess INTEGER,
#WeekdayOfDateToProcess INTEGER,
#MondayOfDateToProcess DATETIME,
#SundayOfDateToProcess DATETIME
SET #ReportDate = '2009-01-01'
print #ReportDate
SET #DateToProcess = '2009-01-26'
--print #DateToProcess
SET #Weekday = (select DATEPART ( dw , #ReportDate ))
--print #Weekday
--print DATENAME(dw, #ReportDate)
SET #NumDaysToMonday =
(SELECT
CASE
WHEN #Weekday = 1 THEN 0
WHEN #Weekday = 2 THEN 1
WHEN #Weekday = 3 THEN 2
WHEN #Weekday = 4 THEN 3
WHEN #Weekday = 5 THEN 4
WHEN #Weekday = 6 THEN 5
WHEN #Weekday = 7 THEN 6
END)
--print #NumDaysToMonday
SET #MondayStartPoint = (SELECT DATEADD (d , -1*#NumDaysToMonday, #ReportDate))
--print #MondayStartPoint
SET #DaysDifference = DATEDIFF ( dd , #MondayStartPoint , #DateToProcess )
--PRINT #DaysDifference
SET #BucketNumber = #DaysDifference/7
--print #BucketNumber
----Calculate the start and end dates of this bucket------
PRINT 'Start Of New Calc'
print #DateToProcess
SET #WeekdayOfDateToProcess = (select DATEPART ( dw , #DateToProcess ))
print #WeekdayOfDateToProcess
SET #NumDaysToMondayOfDateToProcess=
(SELECT
CASE
WHEN #WeekdayOfDateToProcess = 1 THEN 0
WHEN #WeekdayOfDateToProcess = 2 THEN 1
WHEN #WeekdayOfDateToProcess = 3 THEN 2
WHEN #WeekdayOfDateToProcess = 4 THEN 3
WHEN #WeekdayOfDateToProcess = 5 THEN 4
WHEN #WeekdayOfDateToProcess = 6 THEN 5
WHEN #WeekdayOfDateToProcess = 7 THEN 6
END)
print #NumDaysToMondayOfDateToProcess
SET #MondayOfDateToProcess = (SELECT DATEADD (d , -1*#NumDaysToMondayOfDateToProcess, #DateToProcess))
print #MondayOfDateToProcess ---This is the start week
SET #SundayOfDateToProcess = (SELECT DATEADD (d , 6, #MondayOfDateToProcess))
PRINT #SundayOfDateToProcess
The problem I see with the one bucket at a time approach is that its hard to make it scale,
If you join into a user defined function you will get better performance, you could use this a a starting point
Why not use a combination of DATEPART(year, date-column) and DATEPART(week, date-column) and group by these values. This works if the week in DATEPART is aligned on Mondays as ISO 8601 requires. In outline:
SELECT DATEPART(year, date_column) AS yyyy,
DATEPART(week, date_column) AS ww,
...other material as required...
FROM SomeTableOrOther
WHERE ...appropriate filters...
GROUP BY yyyy, ww -- ...and other columns as necessary...

Resources