How to convert weekly data into monthly data in sql? - sql

I have a table that has weekly effort hrs and I need to report data on a monthly basis. for most of the cases, the week start date and end date lie in the same month and those records are easy to aggregate but when the week start date and end date don't lie in the same month then I need to break that week's records into 2 rows. One with an actual start date and end of the month as the end date and another record as 1st of next month as the start date and actual end date as the end date.
For the hrs calculation, I need to calculate the number of days that fall in the first month then divide the total hrs by 5 and then multiply by the number of days. and for the second record, it will be total hrs by 5 and then multiply by (5-number of days)
SELECT [USERNAME]
,[EMPLOYEE_NAME]
,[EFFORT_HRS]
,[TS_START_DATE]
,[TS_END_DATE]
FROM [dbo].[Source]
OUTPUT:
USERNAME
EMPLOYEE_NAME
Task
EFFORT_HRS
TS_START_DATE
TS_END_DATE
mk
xyz
abcdefg
40
12/27/2021
1/2/2022
mk
xyz
defgh
33.5
1/31/2022
2/6/2022
mk
xyz
abcdefg
6
4/25/2022
5/1/2022
Expected Result:
USERNAME
EMPLOYEE_NAME
Task
EFFORT_HRS
TS_START_DATE
TS_END_DATE
mk
xyz
abcdefg
40
12/27/2021
12/31/2021
mk
xyz
abcdefg
0
1/1/2022
1/2/2022
mk
xyz
defgh
6.7
1/31/2022
1/31/2022
mk
xyz
defgh
26.8
2/1/2022
2/6/2022
mk
xyz
abcdefg
6
4/25/2022
4/30/2022
mk
xyz
abcdefg
0
5/1/2022
5/1/2022

What you are NOT providing is some sort of calendar associated with holidays such as Jan 1, 2021 (Friday), and Jan 2, 2021 (Saturday) which do not appear to be paid out. That being said, I will leave you to either provide additional information to exclude such dates, or figure out how to adjust the query.
I would also suggest a better storage solution for time be done on a daily basis for easier querying and inclusion / exclusion such as holidays. But, from what is given I have below sample to create and populate table per your sample data.
create table Source
( UserName nvarchar(5),
Employee_Name nvarchar(5),
Task nvarchar(10),
Effort_Hrs numeric( 5, 2),
TS_Start_Date datetime,
TS_End_Date datetime )
insert into Source
( UserName,
Employee_Name,
Task,
Effort_Hrs,
TS_Start_Date,
TS_End_Date)
values
( 'mk', 'xyz', 'abcdefg', 40, '2021-12-27', '2022-01-02' ),
( 'mk',' xyz', 'defgh', 33.5, '2022-01-31', '2022-02-06' ),
( 'mk',' xyz', 'abcdefg', 6, '2022-04-25', '2022-05-01' )
And now the query itself. No matter what you do based of the existing data, you will need a UNION. Basically, selecting the same columns of data in each query. First, get all records once, but chop-off the end date to the last of the month if the start and end dates are different months.
The UNION part will only consider those records where the start and end dates are DIFFERENT months. So, if you had a work week of Jan 5 - Jan 11, you would only see it in the single record with no split, but in your other examples that cross months, you get TWO records. One for the first month, one for the second.
I have rewritten the query based on your feedback of week days and ignoring that of weekends. As such, when computing the pay earned, I had to apply a case/when if PayDays = 0, to just return 0 hours, otherwise you would get a divide by zero error
I am adding extra columns (which you can remove), so you can see how / where the pieces come into play. Now, for ALL records, the start date IS the real basis of the start date in final output. In the first query, its straight-forward, it IS the start date. However, in the UNION portion query, the start date is the first of the month, but again, only in the second query the months are different. So for that, I am doing date add (net subtract) 1 less than the day of the month of the ending date.
I also changed to use EOMONTH() call to compute the end of month for a given date such as in the first part of union where the end date crosses into following month vs the dateadd() originally used.
For the pay days in the UNION part of the query, which only represents entries that cross into the following month, the PayDays IS the ending date Days. So Feb 6th would be 6 days.
NOT month( ts_start_Date ) = month( ts_end_Date )
Here is a function to compute your work days within a given time period begin/end dates that forces cycle through each individual day to determine Mon-Fri.
CREATE Function dbo.WorkDaysInWeek
( #pStartDate as datetime,
#pEndDate as datetime)
RETURNS int
AS
BEGIN
-- if bad start/end date because they passed in end date first, swap them
if( #pStartDate > #pEndDate )
begin
declare #holdDate as DateTime
set #holdDate = #pStartDate
set #pStartDate = #pEndDate
set #pEndDate = #holdDate
end
-- convert to just date to prevent false calculations on time consideration
set #pStartDate = convert( date, #pStartDate)
set #pEndDate = convert( date, #pEndDate )
declare #workDays as int
set #workDays = 0
WHILE ( #pStartDate <= #pEndDate)
BEGIN
-- is the current day being tested a week day vs weekend. Only count Mon-Fri
if( datepart( weekday, #pStartDate) in ( 2, 3, 4, 5, 6 ))
set #workDays = #workDays + 1
set #pStartDate = dateadd( day, 1, #pStartDate )
END
RETURN #workDays
end
GO
Now, instead of computing the datediff in days, you call the function with begin and end dates and the function will cycle through each day individually to determine Mon-Fri only to be counted
Here is the final query.
select
AllWork.*,
case when AllWork.PayDays = 0
then 0.0
else ( AllWork.EFFORT_HRS / ( 1.0 * dbo.WorkDaysInWeek( AllWork.TS_Start_Date, AllWork.TS_End_Date))) * AllWork.PayDays end HoursInPayPeriod
from
(SELECT
USERNAME,
EMPLOYEE_NAME,
EFFORT_HRS,
TS_START_DATE,
TS_END_DATE,
TS_START_DATE as RealStart,
case when month( ts_start_Date ) = month( ts_end_Date )
then ts_end_date
-- simplified date add to use build-in function EOMONTH (End of Month)
else EOMONTH( ts_start_Date, 0) end RealEnd,
dbo.WorkDaysInWeek( TS_START_DATE,
case when month( ts_start_Date ) = month( ts_end_Date )
then ts_end_date
-- simplified date add to use build-in function EOMONTH (End of Month)
else EOMONTH( ts_start_Date, 0) end ) PayDays
FROM
Source
UNION
-- union to get all entries where the end date is a new month from the start
SELECT
USERNAME,
EMPLOYEE_NAME,
EFFORT_HRS,
TS_START_DATE,
TS_END_DATE,
dateadd( day, 1 - datepart( day, ts_end_Date ), ts_end_Date ) RealStart,
ts_end_date RealEnd,
dbo.WorkDaysInWeek( dateadd( day, 1 - datepart( day, ts_end_Date ), ts_end_Date ),
ts_end_date ) PayDays
FROM
Source
where
-- only care about those entries where the start and end date are different months
NOT month( ts_start_Date ) = month( ts_end_Date )
) AllWork

Related

Calculate business hours in SQL

How can I calculate business hours between two date and times? For example we have two dates; 2022-01-21 16:35:02.470 and 2022-01-24 09:15:02.293
The working hours are 08:00 to 19:00 on weekdays
How can I calculate working hours with SQL?
If we assume that the FROM and TO dates are on the same row:
With Tbl as (
select FROM_DATE=cast('2022-01-21 16:35:02.470' as datetime),
TO_DATE=cast('2022-01-24 09:15:02.293' as datetime)
),
--Build a calendar between two dates
Cal as (
select DATE_D=cast(FROM_DATE as date), TO_DATE_D=cast(TO_DATE as date)
from Tbl
union all
select DATE_D=dateadd(day,1,Cal.DATE_D), Cal.TO_DATE_D
from Cal
where Cal.DATE_D<=Cal.TO_DATE_D
),
-- Eliminate weekends and non-working hours in the calendar
WorkDays as (
select WDCAL_FROM_DATE=dateadd(hour, 8, cast(DATE_D as datetime)),
WDCAL_TO_DATE=dateadd(hour, 19, cast(DATE_D as datetime))
from Cal
where datediff(day, '1900-01-06', DATE_D) % 7 not in (0,1) -- Saturdays, and Sundays
)
-- Find overlapping intervals, and calculate seconds in between, aggregate and convert to hours.
select
Hrs=sum(
datediff(second,
case when T.FROM_DATE>=WD.WDCAL_FROM_DATE then T.FROM_DATE else WD.WDCAL_FROM_DATE end, -- Final interval FromDT
case when T.TO_DATE<=WD.WDCAL_TO_DATE then T.TO_DATE else WD.WDCAL_TO_DATE end -- Final interval ToDT
)
) / 3600.
from Tbl T
inner join
WorkDays WD
on T.FROM_DATE<=WD.WDCAL_TO_DATE
and WD.WDCAL_FROM_DATE<=T.TO_DATE
gives number of hours, as a decimal fraction, within working hours.
If you need to take public holidays into account, I suggest that you first build a calendar table (instead of dynamically generating it).

How can I get the last day of periodic months in SQL?

I am preparing the next 5 months date according to the value given in the query I wrote.
DECLARE #StartDate DATETIME = '2022-03-31', #monthadd INT = 5;
; WITH dates AS (
SELECT #StartDate [vade]
UNION ALL
SELECT DATEADD(MONTH,1,[vade])
FROM dates
WHERE DATEADD(MONTH,1,[vade]) <= DATEADD(MONTH,#monthadd,#StartDate)
)
SELECT *
FROM dates
OPTION (MAXRECURSION 0)
GO
However, when the last day of the month is 31, it is necessary to list the last day, which is the nearest day, in the following months. how do i do this?
Actual results
vade
2022-03-31 00:00:00.000
2022-04-30 00:00:00.000
2022-05-30 00:00:00.000
2022-06-30 00:00:00.000
2022-07-30 00:00:00.000
2022-08-30 00:00:00.000
Edit:
This is a maturity plan. If the person makes installments on the 31st of the month, the payment must be made on the last day of each month. If he does it on the 30th, the month should have 30 if it has 30 days, 30 if it has 31 days, and 29 if it has 29 days. If maturity starts on the 20th, it must be the 20th of each month. Imagine you take out a loan on the 30th of the month. If the month is 29 days, they will ask you to pay on the 29th day, and if the month is 31 days, they will ask you to pay on the 30th day. I know it's very confusing and I'm sorry about that.
Updated 2022-04-01
If I'm understanding correctly, you want to return the same "day" for each month - except when #StartDate is the last day of the month.
One approach would be to determine if the #StartDate is the last day of the month. If so, use EOMONTH() to return the last day in each of the subsequent months. Otherwise, use DATEADD() to return the specified "day" in each month. This approach should work for any date.
One approach is as follows:
If Maturity Date is last day of month, OR Maturity Day of month is > number of days in subsequent month, use EOMONTH() to return the last day of that month
Otherwise, use DATEADD() and DATEFROMPARTS() to generate the next date using the Maturity Day of month
SQL:
-- Note: Using 12 months for demo only
; WITH dates AS (
SELECT #StartDate AS MaturityDate
, IIF(#StartDate = EOMONTH(#StartDate), 1, 0) AS IsEOM
UNION ALL
SELECT
CASE -- Maturity date is last day of month OR
-- Maturity "day" is > number of days in current month
WHEN IsEOM = 1 OR DAY(#StartDate) > DAY( EOMONTH(NextMaturityDate) )
THEN EOMONTH( DATEADD(MONTH, 1, MaturityDate ))
-- Otherwise, maturity "day" is valid for current month
ELSE DATEFROMPARTS(
Year(NextMaturityDate)
, Month(NextMaturityDate)
, DAY(#StartDate)
)
END
, IsEOM
FROM ( SELECT MaturityDate
, IsEOM
, DATEADD(MONTH, 1, MaturityDate) AS NextMaturityDate
FROM dates
) t
WHERE MaturityDate < #EndDate
)
SELECT MaturityDate AS [vade]
FROM dates
OPTION (MAXRECURSION 0)
Results for 2022-03-31 (Last Day Of Month)
vade
2022-03-31
2022-04-30
2022-05-31
2022-06-30
2022-07-31
2022-08-31
2022-09-30
2022-10-31
2022-11-30
2022-12-31
2023-01-31
2023-02-28
2023-03-31
2023-04-30
2023-05-31
2023-06-30
Results for 2022-03-30 (NOT Last Day Of Month)
vade
2022-03-30
2022-04-30
2022-05-30
2022-06-30
2022-07-30
2022-08-30
2022-09-30
2022-10-30
2022-11-30
2022-12-30
2023-01-30
2023-02-28
2023-03-30
2023-04-30
2023-05-30
2023-06-30
db<>fiddle here
The DATEADD function already takes into account of the corner cases, like the end of the month, so you don't need to handle it.
In order to have a cleaner code, you can lay down a stored procedure, that creates (or replaces) a dates_list table and then cycles over the number of months to add to the start date.
DELIMITER //
CREATE OR REPLACE PROCEDURE create_dates_list (
IN start_date DATETIME,
IN num_months INT
)
BEGIN
DECLARE idx INT DEFAULT 0;
CREATE OR REPLACE TABLE dates_list (
date DATE
);
WHILE idx <> num_months DO
INSERT INTO tab VALUES(
DATEADD(#start_date, INTERVAL #idx MONTH)
);
SET idx = idx + 1;
END WHILE;
END //
DELIMITER ;
When you need to obtain new dates, you can refresh that table by setting the parameters and calling the stored procedure:
DECLARE #StartDate DATETIME = '2022-03-31', #monthadd INT = 5;
CALL create_dates_list(#StartDate, #monthadd);
You can freely access the table anytime by using the tools that sql empowers you with.
If you don't need the table to exist for further sessions, you can define the table as TEMPORARY. The official documentation on temporary tables is very detailed and comprehensive of examples, check it out to get to know more about it.

Group by start and end of the week date

I have the below promotion dates:
Group: Ambient
Start Date: 03/11/2020 End Date: 09/11/2020
Start Date: 10/11/2020 End Date: 16/11/2020
Group: Chilled
Start Date: 04/11/2020 End Date: 10/11/2020
Start Date: 11/11/2020 End Date: 17/11/2020
etc
My data consists of the below columns:
Dates (Daily), Group (Ambient/Chilled), Net Sales, Volume Sold
I want to group the data like the below:
Weekly Date, Group, Net Sales, Volume Sold
The Weekly date I want the week to start from Tuesday and ending on Monday based on the type of group it is. If the product is chilled I want the week to start from Wednesday and end on Tuesday. I want to do this and include a historical view to compare previous weeks to promotional weeks Example
Week Date Group Net Sales Volume
Row 1 - 27/10/2020 Ambient £900 30
Row 2 - 03/11/2020 Ambient £1000 50
Row 3 - 04/11/2020 Chilled £2000 40
Row 4 - 11/11/2020 Chilled £1000 30
What is the SQL query for changing the week dates?
There might be other ways to do this, but I would it first creating some functions that return me the DOW (Day Of Week) as I wanted (for Tuesday 0, ... Monday 6) and then another function FDOW (First Day Of Week) that use DOW. ie:
-- Tuesday = 0...Monday = 6
CREATE FUNCTION [dbo].[DOW]
(
#date DATETIME
)
RETURNS INT
AS
BEGIN
-- Add the T-SQL statements to compute the return value here
RETURN (##DATEFIRST + DATEPART(dw, #date) - 1 - 2) % 7;
END;
CREATE FUNCTION [dbo].[FDOW]
(
#date DATETIME
)
RETURNS DATETIME
AS
BEGIN
RETURN DATEADD(d, -dbo.DOW(#date), CAST(#date AS DATE));
END;
Having such functions it is easy to group on "WeekStart" which is our FDOW function:
Select dbo.FDOW(Dates) as WeekOf, [Group],
sum([Net Sales]) [Net Sales],
sum([Volume Sold]) [Volume]
from myTable
group by dbo.FDOW(Dates) as WeekOf, [Group];

How to get all the dates in a full calendar month

In a calendar control, we can see some dates from the previous month and next month also. Sample image below
(ie Apr-2016: Starts from Mar-28 and ends in May-08
Mar-2016: Starts from Apr Feb-29 and ends in Apr-10)
Here, i need to generate a list of all the dates in a calendar control for a particular year month. My week start is Monday.
Here is the tsql script i have tried so far.
DECLARE #V_DATE DATE = GETDATE()
;WITH CTE_DATE AS (
SELECT DATEADD(dd,-(DAY(#V_DATE)-1),#V_DATE) CDATE
UNION ALL
SELECT DATEADD(dd,1,CDATE)
FROM CTE_DATE
WHERE DATEADD(dd,1,CDATE) <= DATEADD(dd,-(DAY(DATEADD(mm,1,CDATE))),DATEADD(mm,1,CDATE))
)
SELECT * FROM CTE_DATE
Result Is:
2016-04-01
2016-04-02
.
.
2016-04-29
2016-04-30
It will list all the days from a inputted year month, but i need to include the
missing dates from the previous month as well as next month.
Expected result for Apr-2016
2016-03-28
2016-03-29
.
2016-04-15
.
2016-05-07
2016-05-08
Expected result for May-2016
2016-04-25
2016-04-26
.
2016-05-15
.
2016-06-04
2016-06-05
Note:- The calendar control is always showing 42 days.
since your week is starts on Monday,you can take referece to date 0 '1900-01-01' which is a Monday. Adding 41 days would gives you your end date
select
date_fr = dateadd(day, datediff(day, 0, '2016-05-01') / 7 * 7, 0),
date_to = dateadd(day, datediff(day, 0, '2016-05-01') / 7 * 7, 41)
the following gives you date 1900-01-01 and Monday
select convert(datetime, 0), datename(weekday, 0)
Have you considered creating a dates table in your database. You would have columns for dates and a column for week number. Linking to this table you could find the week number for your start and end dates, you could then re-link to the table to find the first date of your start week and the last date of your end week. This would probably be more efficient than calculations at each step each time, it is a simple link.
I have create done script for this. This is working as per my expectation, may be helpful for future reference. (Thanks #Squirrel for the logic).
DECLARE #V_ST_DATE DATE = GETDATE()
SET #V_ST_DATE = DATEADD(DAY,-(DAY(#V_ST_DATE)-1),#V_ST_DATE)
SET #V_ST_DATE = DATEADD(WEEK,DATEDIFF(WEEK, 0, #V_ST_DATE) ,0) +
(CASE WHEN DATEADD(WEEK,DATEDIFF(WEEK, 0, #V_ST_DATE) ,0) > #V_ST_DATE THEN -7 ELSE 0 END)
;WITH CTE_DATE AS (
SELECT #V_ST_DATE CDATE,0 TDAYS
UNION ALL
SELECT DATEADD(DAY,1,CDATE) , DATEDIFF(DAY,#V_ST_DATE, DATEADD(DAY,1,CDATE))
FROM CTE_DATE
WHERE DATEDIFF(DAY,#V_ST_DATE, DATEADD(DAY,1,CDATE)) < 42
)
SELECT * FROM CTE_DATE

How to get Monday, Sunday based on a Week Number in SQL Server?

I have a week number (e.g. 23) and I'd like to get the dates for Monday and Sunday of that week.
I am assuming Monday as the first day of the week (e.g. I have SET DATEFIRST 1 in my script).
DECLARE #startweek1 datetime
SET DATEFIRST 1
--first monday of year
SELECT #startweek1 = DATEADD(day, 8-DATEPART(weekday, '2011-01-01'), '2011-01-01')
--day based
SELECT
DATEADD(day, 22 * 7, #startweek1) AS MondayWeek23,
DATEADD(day, 23 * 7 -1 , #startweek1) AS SundayWeek23
--week based
SELECT
DATEADD(week, 22, #startweek1) AS MondayWeek23,
DATEADD(day, -1, DATEADD(week, 23 , #startweek1)) AS SundayWeek23
Edit:
This solution works if week 1 does not start on day 1 as Andomar said
Edit 2:
According to Wikipedia: 2008-12-29 is day 1 of week 1 of 2009 in ISO.
And week numbers vary as Andomar said
Cyberkiwi mentioned this code is wrong for 2007: it's wrong far more often than that. The same applies to his code too which matches 2007 but is equally wrong for the rest.
Declare #StartDate datetime;
Set #StartDate = '20110101';
With StartOfWeek As
(
Select DateAdd(
week
, 23
, DateAdd(
d
, -(DatePart(dw, #StartDate) - 1)
, #StartDate
) ) As Sunday
)
Select Sunday, DateAdd(d,1,Sunday) As Monday
From StartOfWeek
We can use the DATEFIRST variable to set the start day of the week. By default, the week starts on Sunday(7). In order to start with Monday, we will set its value to 1.
# Printing default value
Select ##DATEFIRST; -- This will give 7 value i.e. default value
# Changing first day of the week to Monday.
Declare #CurrentWeekNumber int
SET DATEFIRST 1
set #CurrentWeekNumber= (SELECT DATEPART(WEEK, GETDATE()))
select #CurrentWeekNumber
PS: Changes to DATEFIRST are valid for current session i.e. it doesn't impact other DB queries which might rely on different week start day.
A calendar table makes this kind of date query pretty simple. A calendar table just has the relevant calendar details calculated and stored at load time instead of calculated at run time. Depending on the application, that can speed things up a lot. This way is dbms agnostic; it just uses literals in the WHERE clause.
select cal_date from calendar
where iso_year = 2011
and iso_week = 23
and cal_dow in ('Mon', 'Sun');
And another way that relies only on having a table of dates.
select cal_date from calendar
where extract(isoyear from cal_date) = 2011
and extract(week from cal_date) = 23
and (extract(isodow from cal_date) = 1 or
extract(isodow from cal_date) = 7);
EXTRACT() is standard SQL, but I'm not sure whether the names of the subfields isoyear, week, isodow are standard SQL.
Note: Any answer that starts with assuming year is 2011 might as well take a static first date, i.e. by replacing #firstMon in my last query below with just the static date '20101227'.
SET DATEFIRST 1
declare #targetYear int
declare #targetWeek int
select #targetYear = 2011, #targetWeek = 23
declare #firstMon datetime
set #firstMon = dateadd(d,1-datepart(dw, right(#targetYear,4)+'0101'),right(#targetYear,4)+'0101')
select
MonOfTargetWeek = dateadd(week, #targetWeek-1, #firstMon),
SunOfTargetWeek = dateadd(week, #targetWeek-1, #firstMon+6)
And the real, final query - which returns a Mon/Sun of NULL when it doesn't exist in that week, such as Mon of partial-week-one or Sun of partial-week-53
;with tmp(Mon, Sun) as (
select
MonOfTargetWeek = dateadd(week, #targetWeek-1, #firstMon),
SunOfTargetWeek = dateadd(week, #targetWeek-1, #firstMon+6)
)
select
RealMonday = case when Year(Mon)=#targetYear then Mon end,
RealSunday = case when Year(Sun)=#targetYear then Sun end
from tmp
For GBN's claim that this answer is wrong (within his answer), I submit below the proof of correctness
I have turned the code into a function.
CREATE function dbo.getMonSunForWeek(#targetYear int, #targetWeek int)
returns table as return
with pre(firstMon) as (
select dateadd(d,1-datepart(dw, right(#targetYear,4)+'0101'),right(#targetYear,4)+'0101'))
, tmp(Mon, Sun) as (
select
dateadd(week, #targetWeek-1, firstMon),
dateadd(week, #targetWeek-1, firstMon+6)
from pre
)
select
Mon, Sun,
RealMonday = case when Year(Mon)=#targetYear then Mon end,
RealSunday = case when Year(Sun)=#targetYear then Sun end
from tmp
GO
And below I present the ENTIRE range of years and weeks (1-53) for the years from 1950 through 2047. In EVERY SINGLE case, when the Monday/Sunday has been determined, and working backwards using DATEPART(week), SQL Server agrees with the week numbering from the function.
set datefirst 1;
select
[Year] = years.number,
[Week] = weeks.number,
[Mon] = fun.realmonday,
[Sun] = fun.realsunday,
[WeekX] = isnull(datepart(week, fun.realmonday), datepart(week, fun.realsunday)),
[MonX] = fun.Mon,
[SunX] = fun.Sun
from master..spt_values years
inner join master..spt_values weeks on weeks.type='P' and weeks.number between 1 and 53
cross apply dbo.getMonSunForWeek(years.number, weeks.number) fun
where years.type='P' and years.number between 1950 and 2047
order by [year], [Week]
Output around the 2005-2006 changeover
when including prior/next year
Year Week Mon-of-week Sun-of-week datepart(week) Mon -and- Sun
2005 52 2005-12-19 2005-12-25 52 2005-12-19 2005-12-25
2005 53 2005-12-26 NULL 53 2005-12-26 2006-01-01
2006 1 NULL 2006-01-01 1 2005-12-26 2006-01-01
2006 2 2006-01-02 2006-01-08 2 2006-01-02 2006-01-08
You might try some dateadd logic...
dateadd(week, 23, '2011-01-01')
dateadd(day, 7, dateadd(week, 23, '2011-01-01'))
UPDATE:
select dateadd(day, 23, dateadd(week, 23, '2011-01-01')) , Datename(weekday,dateadd(day, 23, dateadd(week, 23, '2011-01-01')));
// RETURNS 7/4/2011, Monday
select Datename(weekday,dateadd(day, 23, dateadd(week, 23, '2011-01-01')));
// RETURNS Monday
EXAMPLE FROM: http://msdn.microsoft.com/en-us/library/ms186819.aspx
DECLARE #datetime2 datetime2 = '2007-01-01 13:10:10.1111111'
SELECT 'year', DATEADD(year,1,#datetime2)
UNION ALL
SELECT 'quarter',DATEADD(quarter,1,#datetime2)
UNION ALL
SELECT 'month',DATEADD(month,1,#datetime2)
UNION ALL
SELECT 'dayofyear',DATEADD(dayofyear,1,#datetime2)
UNION ALL
SELECT 'day',DATEADD(day,1,#datetime2)
UNION ALL
SELECT 'week',DATEADD(week,1,#datetime2)
UNION ALL
SELECT 'weekday',DATEADD(weekday,1,#datetime2)
UNION ALL
SELECT 'hour',DATEADD(hour,1,#datetime2)
UNION ALL
SELECT 'minute',DATEADD(minute,1,#datetime2)
UNION ALL
SELECT 'second',DATEADD(second,1,#datetime2)
UNION ALL
SELECT 'millisecond',DATEADD(millisecond,1,#datetime2)
UNION ALL
SELECT 'microsecond',DATEADD(microsecond,1,#datetime2)
UNION ALL
SELECT 'nanosecond',DATEADD(nanosecond,1,#datetime2);
/*
Year 2008-01-01 13:10:10.1111111
quarter 2007-04-01 13:10:10.1111111
month 2007-02-01 13:10:10.1111111
dayofyear 2007-01-02 13:10:10.1111111
day 2007-01-02 13:10:10.1111111
week 2007-01-08 13:10:10.1111111
weekday 2007-01-02 13:10:10.1111111
hour 2007-01-01 14:10:10.1111111
minute 2007-01-01 13:11:10.1111111
second 2007-01-01 13:10:11.1111111
millisecond 2007-01-01 13:10:10.1121111
microsecond 2007-01-01 13:10:10.1111121
nanosecond 2007-01-01 13:10:10.1111111
*/