How to retrieve repetitive data in sql without using loops? - sql-server

I have a help-desk system in which there is a table with ticket details. I have a table in SQL server with data something like this,
ID status create_date closing_date
---------------------------------------------------------
1 closed 2015-01-01 14:06:30 2015-01-02 18:06:42
2 closed 2015-01-01 15:16:42 2015-01-02 08:12:10
- - - -
- - - -
Now I want to display the number of hours taken to close the tickets in past 5 weeks looking at last 2 months.
For ex., I want to show data for past 5 weeks from today (49,50,51,52,1) and each week should look at the last 2 months closed tickets. (for week 49, the data should be between week 49 - 60 days and week 49 end date).
Currently, I have used loop to get the result like this,
declare #week_start datetime, #week_end datetime, #week int, #last_week int, #week_number int
set #last_week = datepart(wk, dateadd(wk, -1, getdate()))
set #week_number = dbo.F_ISO_WEEK_OF_YEAR(dateadd(wk, -5, getdate()))
set #week = #last_week - 4
set #week_start = dateadd(day, -60, dateadd(wk, -5, DATEADD(week, DATEDIFF(day, 0, getdate())/7, 4)))
set #week_end = dateadd(wk, -5, DATEADD(week, DATEDIFF(day, 0, getdate())/7, 4))
while #week <= #last_week
begin
select #week_number as week_number,
cast(avg(cast(Datediff(hour,create_date,closing_date) as decimal (10,2))) as decimal(10,2)) Hours
FROM tickets
where closing_date between #week_start
and #week_end
--group by dbo.F_ISO_WEEK_OF_YEAR(closing_date )
set #week_start = dateadd(wk, 1, #week_start)
set #week_end = dateadd(wk, 1, #week_end)
set #week = #week + 1
set #week_number = #week_number + 1
end
which returns the following data,
week_number Hours
--------------------
49 121.56
50 129.06
51 125.57
52 125.90
53 130.52
Now, I do not want to use the loop because it messes up the week numbers and I also want to display ISO week numbers.
So here is my modified code,
select dbo.F_ISO_WEEK_OF_YEAR(closing_date) as week_number,
cast(avg(cast(Datediff(hour,create_date,closing_date) as decimal (10,2))) as decimal(10,2)) Hours
FROM tickets
where closing_datebetween DATEADD(wk, -5, getdate()) and DATEADD(wk, -1, getdate())
group by dbo.F_ISO_WEEK_OF_YEAR(closing_date)
But it returns the following data which is incorrect as it's not looking at the past 2 months from a particular week,
week_number Hours
--------------------
49 142.69
50 262.76
51 95.50
52 85.39
1 75.90
I do not know how can I modify my query to get the same result without using loops.
Any help will be appreciated.
Thanks.

It's a little hard to gain full context without seeing what your underlying data it, but my first guess would be that in your iterative code, you have a sliding window (i.e. 5 weeks ago you're looking at past 2 months + 5 week, 4 weeks ago, past 2 month +4 , etc). Your select statement is statically looking at the values as of one point in time.
If you're running SQL Server 2012+ you can use row frames to define such a sliding windows (http://msdn.microsoft.com/en-us/library/ms189461.aspx). Alternatively, and I say this with caution, if you only need to return 5 rows, hitting the same table 5 times for a report like this isn't that bad. If you can provide sample data, you may be able to get some more code-specific replies.

Related

How can I make a dynamic SQL Server query with sliding dates but specific times?

I'm trying to make a dynamic query which searches records from 9 days ago at 21:00 to 2 days ago at 23:00.
I've been asked to make a weekly report in Maximo which lists all records between Sundays at 21:00 and the following Fridays at 23:00. The report must generate each week on Tuesdays at 6:00. I have pieced something together from other queries but can't figure out where to go from here:
startdatetime >= dateadd(hour, 21, dateadd(dd, datediff(dd, 0, getdate()), 0))
and startdatetime <= dateadd(hour, 23, dateadd(dd, datediff(dd, 0, getdate()), 0))
I've used DATEADD before to show all records between certain times of the current day. What I can't figure out is how to get those times for previous dates. I tried doing "...dateadd(dd,datediff(dd,0,getdate()-9),0) and ...dateadd(dd,datediff(dd,0,getdate()-2),0)", but I got a SQL error code 207. I obviously don't understand how the second DATEADD segment works. Can anyone show me what I'm doing wrong?
You're really close, you just need to change the second zero to the days you want to move.
WHERE startdatetime >= dateadd(hour,21,dateadd(dd,datediff(dd,0,getdate()),-9))
AND startdatetime <= dateadd(hour,23,dateadd(dd,datediff(dd,0,getdate()),-3))
The error 207 refers to an invalid column name. Make sure that you don't have any typos on your query and that the columns actually exist.
Using 0 as a base (which equates to 1900-01-01) is not very intuitive or self-documenting. Also writing a query that assumes it will only ever run on a Tuesday is a bit dangerous; we can make it so that if it needs to be run again on Wednesday or Thursday it still yields the right results.
-- first, let's make sure we know what weekday is a Sunday
DECLARE #OriginalDateFirst int = ##DATEFIRST;
IF #OriginalDateFirst <> 7
BEGIN
SET DATEFIRST 7;
END
-- let's figure out today
DECLARE #today datetime = CONVERT(date, GETDATE()),
#weekday int = DATEPART(WEEKDAY, GETDATE());
-- if weekday is 3 (Tuesday), the previous Sunday is today -2,
-- and the Sunday before that is today -9
-- if weekday is 4 (Wednesday), the days are -3, -10
-- if weekday is 5 (Thursday), the days are -4, -11
-- and so on
-- so the range is -6-#weekday to 1-#weekday
DECLARE #MostRecentSunday datetime = DATEADD(HOUR, 23, DATEADD(DAY, 1-#weekday, #today)),
#PreviousSunday datetime = DATEADD(HOUR, 21, DATEADD(DAY, -6-#weekday, #today));
-- make sure this is what you want:
SELECT #PreviousSunday, #MostRecentSunday;
-- now your query just says:
... WHERE startdatetime >= #PreviousSunday
AND startdatetime <= #MostRecentSunday;
-- let's put the datefirst setting back
IF #OriginalDateFirst <> 7
BEGIN
SET DATEFIRST #OriginalDateFirst;
END
Yes, it's definitely more code (though you can condense it quite a bit if you like, and if you know nobody messes with DATEFIRST, you can clear out some of that logic, too).
But I'm a big fan of making code self-documenting vs. terse/cryptic.

Finding Months Difference in Days

I want to find the difference between two dates which should be exact in months like if the date difference is greater than 182 days them on 183rd Day it should show as 7 Months.I tried below one,
SELECT ROUND(cast(DATEDIFF(DD,'2018-01-01 18:45:30.203',GETDATE()) as float)/30,0)
but it has 15 days difference.
I wouldn’t use 30. It’s fail on some months. For example Jan 1 and March 2 since February doesn’t have at least 30 days. But I think this is what you are after. If the current day isn’t the first of the month then add a month.
SELECT
Case
when datepart(day,getdate()) > 1
Then datediff(month,'2018-01-01 18:45:30.203',GETDATE()) + 1
Else datediff(month,'2018-01-01 18:45:30.203',GETDATE())
End
I think that calculating the difference in months as an integer is very similar to calculating the age of a person. We can take the DATEDIFF in months, add this number of months to the first date and compare that to the second date to decide whether we have to subtract 1 from the difference:
DECLARE #Date1 datetime = '2018-01-01 18:45:30.203';
DECLARE #Date2 datetime = GETDATE();
SELECT
CASE
WHEN DATEADD(month, DATEDIFF(month, #Date1, #Date2), #Date1) > #Date2
THEN DATEDIFF(month, #Date1, #Date2) - 1
ELSE DATEDIFF(month, #Date1, #Date2)
END

Convert number of hours to days and hours in SQL Server (NOT T-SQL)

I have a number of hours which I need to display in the format of days and hours.
This number is derived from a DATEDIFF instruction.
For numbers less than 24, I wish to display only hours - ie, 21 hours.
For larger numbers, I wish to display days and hours - ie, 3 days, 14 hours
I do not need to display any smaller unit than hours, and values should be rounded down to the preceding hour, so 1 hour and 59 minutes will be 1 hour.
I cannot use a stored procedure - this must run as a single select statement.
I am aware that I can calculate the value by using modulo, so assuming 71 hours:
select concat((71 - (71 % 24)) / 24, ' days, ', 71 % 24, ' hours')
This however is somewhat messy, and as the statement must be a single select, I will have to calculate the DATEDIFF 3 times as below.
SELECT CONCAT (
(DATEDIFF(HOUR, StartDate, EndDate) -
(DATEDIFF(HOUR, StartDate, EndDate) % 24)) / 24,
' days, ',
DATEDIFF(HOUR, StartDate, EndDate) % 24,
' hours')
FROM RecordsTable
Is it possible to either format a number of hours as days and hours directly using an inbuilt SQL command, or failing that, select (datediff(hour, StartDate, EndDate) into a variable which I can reuse in the single select?
EDIT - As suggested, the solution was to use a CTE as follows:
WITH totalhours (htotal) AS
(
SELECT
DATEDIFF(HOUR, StartDate, EndDate) AS htotal
FROM
RecordsTable
)
SELECT
CONCAT ((htotal - (htotal % 24)) / 24,
' days, ',
htotal % 24,
' hours')
FROM
RecordsTable;
Use a CTE to generate your total once, and reference that total in your select against the CTE. Or use a subquery to generate the total once and then select from the subquery to get the desired results.
The fundamental issue is you need to materialize the total once to be able to reference it; forcing the engine to materialize a value is generally done via a CTE or subquery.
You can do a lot with datetime objects and format strings or datepart. For example,
declare #n int = 105;
select format(dateadd(day, -1, dateadd(hour, #n, '1753-1-1')), 'd h');
-- 4 9
Taking the minimum datetime value (1753-01-01), adding the requisite number of hours, subtracting one day (because on the first day you want days = 0), and then formatting.
You could improve the formatting like this:
select format(dateadd(day, -1, dateadd(hour, #n, '1753-1-1')), 'd \da\y(\s), h \hour(\s)');
-- 4 day(s), 9 hour(s)
Of course this will only work up to 31 days, because then you'll be out of the month of January in 1753 and into February. If that's the case, revert to datepart. This is uglier, but will work for larger values
select
datepart(day, (dateadd(day, -1, dateadd(hour, #n, '1753-1-1')))),
datepart(hour, (dateadd(day, -1, dateadd(hour, #n, '1753-1-1'))));

SQL Server: convert to today then add 8 hours: part II

In my older post, I tried to change the date to current date and add 8 hours time.
SQL Server: convert to today then add 8 hours
UPDATE business.dbo.db_schedule
SET nextUpdate= DATEADD(hh, 8,
DATEADD(d, DATEDIFF(D,nextUpdate,Getdate()),
nextUpdate))
where sno=8
note: nextUpdate may be any time in the past.
if I want to go one step further; I want nextdate to be in the future time.
Because if I add 8 hour once, the result may be still a time in the past, I may have to add one more (or even twice) so the result is in the future. Can i do it in one step?
UPDATE business.dbo.db_schedule
SET nextUpdate =
DATEADD(HOUR
, 8 * CEILING((DATEDIFF(MILLISECOND, nextUpdate, GETDATE()) / 3600000.) / 8.)
, nextUpdate)
)
WHERE sno = 8;
Instead of just adding 8 hours, we multiply 8 by the CELING of the number of hours apart nextUpdate is from GETDATE()
e.g. If nextUpdate is 2012-10-01 02:30:00 and GETDATE() is currently 2012-10-02 15:15:00, then there are 36.75 (rounded up to 37) Hours difference. The CEILING of 37 / 8 is 5, which means you will need to add 5 * 8 hours to get a time that is after GETDATE() yet retains the multiple of 8 hours from your current nextUpdate value.
SQLFiddle example of the solution
I think this, although a lot to look at, will actually perform better, since it is just a bunch of calculations in a single pass through the data.
UPDATE business.dbo.db_schedule
SET nextUpdate=
CASE
WHEN
DATEADD(hh, 8, DATEADD(d, DATEDIFF(
D,nextUpdate,Getdate()), nextUpdate))
> Getdate() THEN
DATEADD(hh, 8, DATEADD(d, DATEDIFF(
D,nextUpdate,Getdate()), nextUpdate))
WHEN
DATEADD(hh, 16, DATEADD(d, DATEDIFF(
D,nextUpdate,Getdate()), nextUpdate))
> Getdate() THEN
DATEADD(hh, 16, DATEADD(d, DATEDIFF(
D,nextUpdate,Getdate()), nextUpdate))
ELSE
DATEADD(d, 1+DATEDIFF(
D,nextUpdate,Getdate()), nextUpdate)
END
where sno=8
Note: the last one is simplified by adding 1 day into the inner expression instead of adding 24 hours at the outer.
Original idea below
The idea expands on the previous one, except that instead of adding just 8 hours, we add all 8, 16 and 24 as 3 separate rows, and use the GROUP BY/MIN to take the one with the earliest time after the current time.
update s
set nextUpdate = a.newTime
from business.dbo.db_schedule s
join
(
select nextupdate, min(nextUpdate2) newTime
from
(
SELECT nextupdate,
nextUpdate2 =
DATEADD(hh, 8*x.y,
DATEADD(d,DATEDIFF(D,
d.nextUpdate,Getdate()),d.nextUpdate))
from business.dbo.db_schedule d
cross join (select 1 union all
select 2 union all
select 3) x(y)
) z
where newTime > getdate()
group by nextupdate
) a on a.nextUpdate = b.nextUpdate;

Get first day of week in SQL Server

I am trying to group records by week, storing the aggregated date as the first day of the week. However, the standard technique I use for rounding off dates does not appear to work correctly with weeks (though it does for days, months, years, quarters and any other timeframe I've applied it to).
Here is the SQL:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);
This returns 2011-08-22 00:00:00.000, which is a Monday, not a Sunday. Selecting ##datefirst returns 7, which is the code for Sunday, so the server is setup correctly in as far as I know.
I can bypass this easily enough by changing the above code to:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);
But the fact that I have to make such an exception makes me a little uneasy. Also, apologies if this is a duplicate question. I found some related questions but none that addressed this aspect specifically.
To answer why you're getting a Monday and not a Sunday:
You're adding a number of weeks to the date 0. What is date 0? 1900-01-01. What was the day on 1900-01-01? Monday. So in your code you're saying, how many weeks have passed since Monday, January 1, 1900? Let's call that [n]. Ok, now add [n] weeks to Monday, January 1, 1900. You should not be surprised that this ends up being a Monday. DATEADD has no idea that you want to add weeks but only until you get to a Sunday, it's just adding 7 days, then adding 7 more days, ... just like DATEDIFF only recognizes boundaries that have been crossed. For example, these both return 1, even though some folks complain that there should be some sensible logic built in to round up or down:
SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31');
SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');
To answer how to get a Sunday:
If you want a Sunday, then pick a base date that's not a Monday but rather a Sunday. For example:
DECLARE #dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, #dt, CURRENT_TIMESTAMP), #dt);
This will not break if you change your DATEFIRST setting (or your code is running for a user with a different setting) - provided that you still want a Sunday regardless of the current setting. If you want those two answers to jive, then you should use a function that does depend on the DATEFIRST setting, e.g.
SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
So if you change your DATEFIRST setting to Monday, Tuesday, what have you, the behavior will change. Depending on which behavior you want, you could use one of these functions:
CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday
(
#d DATE
)
RETURNS DATE
AS
BEGIN
RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', #d), '19050101'));
END
GO
...or...
CREATE FUNCTION dbo.StartOfWeek2 -- always the DATEFIRST weekday
(
#d DATE
)
RETURNS DATE
AS
BEGIN
RETURN (SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, #d), #d));
END
GO
Now, you have plenty of alternatives, but which one performs best? I'd be surprised if there would be any major differences but I collected all the answers provided so far and ran them through two sets of tests - one cheap and one expensive. I measured client statistics because I don't see I/O or memory playing a part in the performance here (though those may come into play depending on how the function is used). In my tests the results are:
"Cheap" assignment query:
Function - client processing time / wait time on server replies / total exec time
Gandarez - 330/2029/2359 - 0:23.6
me datefirst - 329/2123/2452 - 0:24.5
me Sunday - 357/2158/2515 - 0:25.2
trailmax - 364/2160/2524 - 0:25.2
Curt - 424/2202/2626 - 0:26.3
"Expensive" assignment query:
Function - client processing time / wait time on server replies / total exec time
Curt - 1003/134158/135054 - 2:15
Gandarez - 957/142919/143876 - 2:24
me Sunday - 932/166817/165885 - 2:47
me datefirst - 939/171698/172637 - 2:53
trailmax - 958/173174/174132 - 2:54
I can relay the details of my tests if desired - stopping here as this is already getting quite long-winded. I was a bit surprised to see Curt's come out as the fastest at the high end, given the number of calculations and inline code. Maybe I'll run some more thorough tests and blog about it... if you guys don't have any objections to me publishing your functions elsewhere.
For these that need to get:
Monday = 1 and Sunday = 7:
SELECT 1 + ((5 + DATEPART(dw, GETDATE()) + ##DATEFIRST) % 7);
Sunday = 1 and Saturday = 7:
SELECT 1 + ((6 + DATEPART(dw, GETDATE()) + ##DATEFIRST) % 7);
Above there was a similar example, but thanks to double "%7" it would be much slower.
For those who need the answer at work and creating function is forbidden by your DBA, the following solution will work:
select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-1), YourDate) as DATE) as WeekStart
From.....
This gives the start of that week. Here I assume that Sundays are the start of weeks. If you think that Monday is the start, you should use:
select *,
cast(DATEADD(day, -1*(DATEPART(WEEKDAY, YouDate)-2), YourDate) as DATE) as WeekStart
From.....
This works wonderfully for me:
CREATE FUNCTION [dbo].[StartOfWeek]
(
#INPUTDATE DATETIME
)
RETURNS DATETIME
AS
BEGIN
-- THIS does not work in function.
-- SET DATEFIRST 1 -- set monday to be the first day of week.
DECLARE #DOW INT -- to store day of week
SET #INPUTDATE = CONVERT(VARCHAR(10), #INPUTDATE, 111)
SET #DOW = DATEPART(DW, #INPUTDATE)
-- Magic convertion of monday to 1, tuesday to 2, etc.
-- irrespect what SQL server thinks about start of the week.
-- But here we have sunday marked as 0, but we fix this later.
SET #DOW = (#DOW + ##DATEFIRST - 1) %7
IF #DOW = 0 SET #DOW = 7 -- fix for sunday
RETURN DATEADD(DD, 1 - #DOW,#INPUTDATE)
END
Maybe you need this:
SELECT DATEADD(DD, 1 - DATEPART(DW, GETDATE()), GETDATE())
Or
DECLARE #MYDATE DATETIME
SET #MYDATE = '2011-08-23'
SELECT DATEADD(DD, 1 - DATEPART(DW, #MYDATE), #MYDATE)
Function
CREATE FUNCTION [dbo].[GetFirstDayOfWeek]
( #pInputDate DATETIME )
RETURNS DATETIME
BEGIN
SET #pInputDate = CONVERT(VARCHAR(10), #pInputDate, 111)
RETURN DATEADD(DD, 1 - DATEPART(DW, #pInputDate),
#pInputDate)
END
GO
Googled this script:
create function dbo.F_START_OF_WEEK
(
#DATE datetime,
-- Sun = 1, Mon = 2, Tue = 3, Wed = 4
-- Thu = 5, Fri = 6, Sat = 7
-- Default to Sunday
#WEEK_START_DAY int = 1
)
/*
Find the fisrt date on or before #DATE that matches
day of week of #WEEK_START_DAY.
*/
returns datetime
as
begin
declare #START_OF_WEEK_DATE datetime
declare #FIRST_BOW datetime
-- Check for valid day of week
if #WEEK_START_DAY between 1 and 7
begin
-- Find first day on or after 1753/1/1 (-53690)
-- matching day of week of #WEEK_START_DAY
-- 1753/1/1 is earliest possible SQL Server date.
select #FIRST_BOW = convert(datetime,-53690+((#WEEK_START_DAY+5)%7))
-- Verify beginning of week not before 1753/1/1
if #DATE >= #FIRST_BOW
begin
select #START_OF_WEEK_DATE =
dateadd(dd,(datediff(dd,#FIRST_BOW,#DATE)/7)*7,#FIRST_BOW)
end
end
return #START_OF_WEEK_DATE
end
go
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47307
CREATE FUNCTION dbo.fnFirstWorkingDayOfTheWeek
(
#currentDate date
)
RETURNS INT
AS
BEGIN
-- get DATEFIRST setting
DECLARE #ds int = ##DATEFIRST
-- get week day number under current DATEFIRST setting
DECLARE #dow int = DATEPART(dw,#currentDate)
DECLARE #wd int = 1+(((#dow+#ds) % 7)+5) % 7 -- this is always return Mon as 1,Tue as 2 ... Sun as 7
RETURN DATEADD(dd,1-#wd,#currentDate)
END
For the basic (the current week's Sunday)
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) as date)
If previous week:
select cast(dateadd(day,-(datepart(dw,getdate())-1),getdate()) -7 as date)
Internally, we built a function that does it but if you need quick and dirty, this will do it.
Since Julian date 0 is a Monday just add the number of weeks to Sunday
which is the day before -1 Eg. select dateadd(wk,datediff(wk,0,getdate()),-1)
I found some of the other answers long-winded or didn't actually work if you wanted Monday as the start of the week.
Sunday
SELECT DATEADD(week, DATEDIFF(week, -1, GETDATE()), -1) AS Sunday;
Monday
SELECT DATEADD(week, DATEDIFF(week, 0, GETDATE() - 1), 0) AS Monday;
Set DateFirst 1;
Select
Datepart(wk, TimeByDay) [Week]
,Dateadd(d,
CASE
WHEN Datepart(dw, TimeByDay) = 1 then 0
WHEN Datepart(dw, TimeByDay) = 2 then -1
WHEN Datepart(dw, TimeByDay) = 3 then -2
WHEN Datepart(dw, TimeByDay) = 4 then -3
WHEN Datepart(dw, TimeByDay) = 5 then -4
WHEN Datepart(dw, TimeByDay) = 6 then -5
WHEN Datepart(dw, TimeByDay) = 7 then -6
END
, TimeByDay) as StartOfWeek
from TimeByDay_Tbl
This is my logic. Set the first of the week to be Monday then calculate what is the day of the week a give day is, then using DateAdd and Case I calculate what the date would have been on the previous Monday of that week.
This is a useful function for me
/* MeRrais 211126
select [dbo].[SinceWeeks](0,NULL)
select [dbo].[SinceWeeks](5,'2021-08-31')
*/
alter Function [dbo].[SinceWeeks](#Weeks int, #From datetime=NULL)
Returns date
AS
Begin
if #From is null
set #From=getdate()
return cast(dateadd(day, -(#Weeks*7+datepart(dw,#From)-1), #From) as date)
END
I don't have any issues with any of the answers given here, however I do think mine is a lot simpler to implement, and understand. I have not run any performance tests on it, but it should be neglegable.
So I derived my answer from the fact that dates are stored in SQL server as integers, (I am talking about the date component only). If you don't believe me, try this SELECT CONVERT(INT, GETDATE()), and vice versa.
Now knowing this, you can do some cool math equations. You might be able to come up with a better one, but here is mine.
/*
TAKEN FROM http://msdn.microsoft.com/en-us/library/ms181598.aspx
First day of the week is
1 -- Monday
2 -- Tuesday
3 -- Wednesday
4 -- Thursday
5 -- Friday
6 -- Saturday
7 (default, U.S. English) -- Sunday
*/
--Offset is required to compensate for the fact that my ##DATEFIRST setting is 7, the default.
DECLARE #offSet int, #testDate datetime
SELECT #offSet = 1, #testDate = GETDATE()
SELECT CONVERT(DATETIME, CONVERT(INT, #testDate) - (DATEPART(WEEKDAY, #testDate) - #offSet))
I had a similar problem. Given a date, I wanted to get the date of the Monday of that week.
I used the following logic: Find the day number in the week in the range of 0-6, then subtract that from the originay date.
I used: DATEADD(day,-(DATEPART(weekday,)+5)%7,)
Since DATEPRRT(weekday,) returns 1 = Sundaye ... 7=Saturday,
DATEPART(weekday,)+5)%7 returns 0=Monday ... 6=Sunday.
Subtracting this number of days from the original date gives the previous Monday. The same technique could be used for any starting day of the week.
I found this simple and usefull. Works even if first day of week is Sunday or Monday.
DECLARE #BaseDate AS Date
SET #BaseDate = GETDATE()
DECLARE #FisrtDOW AS Date
SELECT #FirstDOW = DATEADD(d,DATEPART(WEEKDAY,#BaseDate) *-1 + 1, #BaseDate)
Maybe I'm over simplifying here, and that may be the case, but this seems to work for me. Haven't ran into any problems with it yet...
CAST('1/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30)) AS DATETIME) + (DATEPART(wk, YOUR_DATE) * 7 - 7) as 'FirstDayOfWeek'
CAST('1/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30)) AS DATETIME) + (DATEPART(wk, YOUR_DATE) * 7) as 'LastDayOfWeek'

Resources