SQL Server, trying to get day of week via a deterministic UDF.
Im sure this must be possible, but cant figure it out.
UPDATE: SAMPLE CODE..
CREATE VIEW V_Stuff WITH SCHEMABINDING AS
SELECT
MD.ID,
MD.[DateTime]
...
dbo.FN_DayNumeric_DateTime(MD.DateTime) AS [Day],
dbo.FN_TimeNumeric_DateTime(MD.DateTime) AS [Time],
...
FROM {SOMEWHERE}
GO
CREATE UNIQUE CLUSTERED INDEX V_Stuff_Index ON V_Stuff (ID, [DateTime])
GO
Ok, i figured it..
CREATE FUNCTION [dbo].[FN_DayNumeric_DateTime]
(#DT DateTime)
RETURNS INT WITH SCHEMABINDING
AS
BEGIN
DECLARE #Result int
DECLARE #FIRST_DATE DATETIME
SELECT #FIRST_DATE = convert(DATETIME,-53690+((7+5)%7),112)
SET #Result = datediff(dd,dateadd(dd,(datediff(dd,#FIRST_DATE,#DT)/7)*7,#FIRST_DATE), #DT)
RETURN (#Result)
END
GO
Slightly similar approach to aforementioned solution, but just a one-liner that could be used inside a function or inline for computed column.
Assumptions:
You don't have dates before
1899-12-31 (which is a Sunday)
You want to imitate ##datefirst = 7
#dt is smalldatetime, datetime,
date, or datetime2 data type
If you'd rather it be different, change the date '18991231' to a date with the weekday that you'd like to equal 1. The convert() function is key to making the whole thing work - cast does NOT do the trick:
((datediff(day, convert(datetime,
'18991231', 112), #dt) % 7)
+ 1)
I know this post is way-super-old, but I was trying to do a similar thing and came up with a different solution and figured I'd post for posterity. Plus I did some searching around and did not find much content on this question.
In my case, I was trying to use a computed column PERSISTED, which requires the calculation to be deterministic. The calculation I used is:
datediff(dd,'2010-01-03',[DateColumn]) % 7 + 1
The idea is to figure out a known Sunday that you know will occur before any possible date in your table (in this case, Jan 3 2010), then calculate the modulo 7 + 1 of the number of days since that Sunday.
The problem is that including a literal date in the function call is enough to mark it as non-deterministic. You can work around that by using the integer 0 to represent the epoch, which for SQL Server is Jan 1st, 1900, a Sunday.
datediff(dd,0,[DateColumn]) % 7 + 1
The +1 just makes the result work the same as datepart(dw,[datecolumn]) when datefirst is set to 7 (default for US), which sets Sunday to 1, Monday to 2, etc
I can also use this in conjunction with case [thatComputedColumn] when 1 then 'Sunday' when 2 then 'Monday' ... etc. Wordier, but deterministic, which was a requirement in my environs.
Taken from Deterministic scalar function to get week of year for a date
;
with
Dates(DateValue) as
(
select cast('2000-01-01' as date)
union all
select dateadd(day, 1, DateValue) from Dates where DateValue < '2050-01-01'
)
select
year(DateValue) * 10000 + month(DateValue) * 100 + day(DateValue) as DateKey, DateValue,
datediff(day, dateadd(week, datediff(week, 0, DateValue), 0), DateValue) + 2 as DayOfWeek,
datediff(week, dateadd(month, datediff(month, 0, DateValue), 0), DateValue) + 1 as WeekOfMonth,
datediff(week, dateadd(year, datediff(year, 0, DateValue), 0), DateValue) + 1 as WeekOfYear
from Dates option (maxrecursion 0)
There is an already built-in function in sql to do it:
SELECT DATEPART(weekday, '2009-11-11')
EDIT:
If you really need deterministic UDF:
CREATE FUNCTION DayOfWeek(#myDate DATETIME )
RETURNS int
AS
BEGIN
RETURN DATEPART(weekday, #myDate)
END
GO
SELECT dbo.DayOfWeek('2009-11-11')
EDIT again: this is actually wrong, as DATEPART(weekday) is not deterministic.
UPDATE:
DATEPART(weekday) is non-deterministic because it relies on DATEFIRST (source).
You can change it with SET DATEFIRST but you can't call it inside a stored function.
I think the next step is to make your own implementation, using your preferred DATEFIRST inside it (and not considering it at all, using for example Monday as first day).
The proposed solution has one problem - it returns 0 for Saturdays. Assuming that we're looking for something compatible with DATEPART(WEEKDAY) this is an issue.
Nothing a simple CASE statement won't fix, though.
Make a function, and have #dbdate varchar(8) as your input variable.
Have it return the following:
RETURN (DATEDIFF(dd, -1, convert(datetime, #dbdate, 112)) % 7)+1;
The value 112 is the sql style YYYYMMDD.
This is deterministic because the datediff does not receive a string input, if it were to receive a string it would no longer work because it internally converts it to a datetime object. Which is not deterministic.
Not sure what you are looking for, but if this is part of a website, try this php function from http://php.net/manual/en/function.date.php
function weekday($fyear, $fmonth, $fday) //0 is monday
{
return (((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, 7, 17, 2006))/(60*60*24))+700000) % 7;
}
The day of the week? Why don't you just use DATEPART?
DATEPART(weekday, YEAR_DATE)
Can't you just select it with something like:
SELECT DATENAME(dw, GETDATE());
Related
We have a requirement to bill our customers per day. We bill for an asset's existence in our system on that day. So, I started with datediff...
select datediff(dd ,'2015-04-24 12:59:32.050' ,'2015-05-01 00:59:59.000');
Returns this:
7
But I need to count the following dates: 4/24,4/25,4/26,4/27,4/28,4/29, 4/30, 5/1, which are 8 days. So datediff isn't quite working right. I tried these variations below
--too simple, returns 7, i need it to return 8
select datediff(dd ,'2015-04-24 12:59:32.050', '2015-05-01 23:59:59.000');
--looking better, this returns the 8 i need
select ceiling(datediff(hh,'2015-04-24 12:59:32.050', '2015-05-01 23:59:59.000')/24.0);
-- returns 7, even though the answer still needs to be 8. (changed enddate)
select ceiling(datediff(hh,'2015-04-24 12:59:32.050', '2015-05-01 00:59:59.000')/24.0);
So, my question... How, in SQL, would I derive the date count like i described, since I believe datediff counts the number of day boundaries crossed.... My current best approach is loop through each day in a cursor and count. Ick.
Use CONVERT to get rid of the time part, add 1 to get the desired result:
SELECT DATEDIFF(dd,
CONVERT(DATE, '2015-04-24 12:59:32.050'),
CONVERT(DATE, '2015-05-01 00:59:59.000')) + 1;
It turns out the time part does not play any significant role in DATEDIFF when dd is used as the datepart argument. Hence, CONVERT is redundant. This:
SELECT DATEDIFF(dd, '2015-04-24 23:59:59.59','2015-05-01 00:00:00.000') + 1
will return 8 as well.
You could try this which would return 8 days.
select datediff(dd ,'2015-04-24 12:59:32.050' ,CASE DATEDIFF(Second,'2015-05-01 00:00:00.000','2015-05-01 23:59:59.000') WHEN 0 THEN '2015-05-01 23:59:59.000' ELSE DATEADD(dd,+1,'2015-05-01 23:59:59.000') END)
If you want to use variables for your dates then something like this would work.
BEGIN
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
DECLARE #EndDateOnly DATE
SET #StartDate = '2015-04-24 12:59:32.050'
SET #EndDate = '2015-05-01 23:59:59.000'
SET #EndDateOnly = CAST(#EndDate AS DATE)
SELECT datediff(dd ,#StartDate ,CASE DATEDIFF(Second,CAST(#EndDateOnly||' 00:00:00.000' AS DATETIME),#EndDate) WHEN 0 THEN #EndDate ELSE DATEADD(dd,+1,#EndDate) END)
END
I have a table in SQL Server which has many rows, with a created_date column. This column has rows starting from the year 2006.
I want to get all the rows which were created in and before February, 2015. This stored procedure has a parameter #month. It should select all the rows based on the #month value entered.
Here is my query:
select *
from products
where 1=1
and year(created_date) <= 2015
and month(created_date) <= #month
But this query returns only the records which were created in and before February month of previous years excluding records which were created in other months of 2014 (e.g., 2014-03-17, 2014-05-05 are excluded).
I have to get a new date based on the #month entered. Suppose I entered month July, I want to have condition "where created_date < 2015-07-31". So I can do something like this,
So I have changed my query,
declare #date datetime
set #date = CAST((2015 + '-' + #month + '-' + 28) as datetime)
select *
from products
where 1=1
and year(created_date) <= 2015
But this query returns 1905-08-08 00:00:00.000 and I want to get 2015-02-28 00:00:00.000 and also I have to find total number of days based on the #month entered so that I can pass that number to CAST((2015 + '-' + #month + '-' + 28) as datetime) instead of 28.
Just use a single date and specify that the created_date column must be less than that date:
declare #newestDate datetime = '2015-03-01'
select *
from products
where created_date < #newestDate
Note that I set the date to be the 1st March but in the query I use < rather than <=. This will cope with a created_date value including a time component, e.g. 2015-02-28 23:59:59
To generate the value of "February of previous year", you may actually be wanting to use the current month of last year, if so, your date would be:
declare #newestDate datetime =
DATEADD(year, -1, DATEADD(month, DATEDIFF(month, 0, GETDATE())+1, 0))
This would then work next month (i.e. March) and would give your query a rolling month.
Always compare date/time fields to a single value where possible -- this is best for performance as well. You can "round" dates with DATEADD and DATEDIFF.
DECLARE #startOfNextMonth DATETIME;
SELECT #startOfNextMonth = DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0);
select * from products where 1=1 and created_date < #startOfNextMonth;
String manipulation to convert dates is also possible, but tends to perform worse and is tricky to get right. This technique applies in general if you want to "round" to years, minutes, 15-second periods, etcetera, which is much harder with strings.
If you can, rewrite your stored procedure to not take a #month parameter but an absolute value that your clients calculate -- it's more general and tends to be easier to work with. Your query then simply reduces to
select * from products where 1=1 and created_date < #limit;
Of course, if you must use a #month, you can construct this offset in the stored procedure itself:
DECLARE #limit DATETIME =
DATEFROMPARTS(DATEPART(YEAR, GETDATE()), #month, 1)
;
This takes advantage of DATEFROMPARTS, which was introduced with SQL Server 2012. For previous versions, reliably constructing a date is considerably messier. There are many wrong ways to do it that will break if the regional settings are set to something unexpected. DATEADD is again of assistance:
DECLARE #limit DATETIME =
DATEADD(MONTH, (DATEPART(YEAR, GETDATE()) - 1900) * 12 + #month - 1, 0)
;
These are not the only methods to construct datetime values, but string manipulation is in any case tricky (because the only reliable format that will not break under regional settings is YYYYMMDD, no dashes).
In this question: Create a date with T-SQL you will see how to construct an sql-server date data type given a certain year and month. Suppose you call that 'my_date'.
You will then be able to do the following:
SELECT * FROM products WHERE created_date < my_date
I have custom table, which stipulates in days how long to retain records for. I need to know how I can pass in a variable into a BETWEEN statement to returns only the records from today inclusive, -variable.
WHERE (MessageDate BETWEEN GETDATE() AND DATEADD(day, DATEDIFF(day, 0, GETDATE()), - 7))
-7 in this instance would be 7 days from today, and is meant to be a parameter which I pass in.
You don't need the DATEDIFF function, you can use just DATEADD, for which the correct parameter order you can find here.
So, your WHERE clause can look like this:
DECLARE #parameter INTEGER
SET #paramenter = -7
...
WHERE
(MessageDate BETWEEN GETDATE ()
AND DATEADD(day, #parameter, GETDATE())
)
Here is a SQLFiddle. (updated fiddle)
Edit:
Also, the BETWEEN part of the query should have an older date first ( DATEADD(day, -7, GETDATE() ) and then a more recent date ( GETDATE() ).
This is why, if you will always have a negative parameter to pass on, you will have to switch the order of the dates in the WHERE clause and use them like this:
WHERE
(MessageDate BETWEEN DATEADD(day, #parameter, GETDATE()
AND GETDATE())
)
But, if you might have to pass both positive and negative parameters, then use this:
WHERE
(MessageDate BETWEEN DATEADD(day, #parameter, GETDATE()
AND GETDATE())
OR
MessageDate BETWEEN GETDATE ()
AND DATEADD(day, #parameter, GETDATE())
)
Using OR you will have both cases covered, the one in which you send a positive parameter and the one where you send a negative parameter.
These cases are mutually exclusive, so only one condition will ever return results.
Substraction from GETDATE() results in DAYS, therefore you can use the following:
DECLARE #param INTEGER
SET #param = 7
--
WHERE cast(MessageDate as datetime) >= GETDATE()- #param
AND cast(MessageDate as datetime)<= GETDATE()
I've been looking around for a chunk of code to find the first day of the current week, and everywhere I look I see this:
DATEADD(WK, DATEDIFF(WK,0,GETDATE()),0)
Every place says this is the code I'm looking for.
The problem with this piece of code is that if you run it for Sunday it chooses the following Monday.
If I run:
SELECT GetDate() , DATEADD(WK, DATEDIFF(WK,0,GETDATE()),0)
Results for today (Tuesday):
2013-05-14 09:36:39.650................2013-05-13 00:00:00.000
This is correct, it chooses Monday the 13th.
If I run:
SELECT GetDate()-1 , DATEADD(WK, DATEDIFF(WK,0,GETDATE()-1),0)
Results for yesterday (Monday):
2013-05-13 09:38:57.950................2013-05-13 00:00:00.000
This is correct, it chooses Monday the 13th.
If I run:
SELECT GetDate()-2 , DATEADD(WK, DATEDIFF(WK,0,GETDATE()-2),0)
Results for the 12th (Sunday):
2013-05-12 09:40:14.817................2013-05-13 00:00:00.000
This is NOT correct, it chooses Monday the 13th when it should choose the previous Monday, the 6th.
Can anyone illuminate me as to what's going in here? I find it hard to believe that no one has pointed out that this doesn't work, so I'm wondering what I'm missing.
It is DATEDIFF that returns the "incorrect" difference of weeks, which in the end results in the wrong Monday. And that is because DATEDIFF(WEEK, ...) doesn't respect the DATEFIRST setting, which I'm assuming you have set to 1 (Monday), and instead always considers the week crossing to be from Saturday to Sunday, or, in other words, it unconditionally considers Sunday to be the first day of the week in this context.
As for an explanation for that, so far I haven't been able to find an official one, but I believe this must have something to do with the DATEDIFF function being one of those SQL Server treats as always deterministic. Apparently, if DATEDIFF(WEEK, ...) relied on the DATEFIRST, it could no longer be considered always deterministic, which I can only guess wasn't how the developers of SQL Server wanted it.
To find the first day of the week's date, I would (and most often do actually) use the first suggestion in #Jasmina Shevchenko's answer:
DATEADD(DAY, 1 - DATEPART(WEEKDAY, #Date), #Date)
DATEPART does respect the DATEFIRST setting and (most likely as a result) it is absent from the list of always deterministic functions.
Try this one -
SET DATEFIRST 1
DECLARE #Date DATETIME
SELECT #Date = GETDATE()
SELECT CAST(DATEADD(DAY, 1 - DATEPART(WEEKDAY, #Date), #Date) AS DATE)
SELECT CAST(#Date - 2 AS DATE), CAST(DATEADD(WK, DATEDIFF(WK, 0, #Date-2), 0) AS DATE)
Results:
---------- ----------
2013-05-12 2013-05-13
SQL Server has a SET DATEFIRST function which allows you to tell it what the first day of the week should be. SET DATEFIRST = 1 tells it to consider Monday as the first day of the week. You should check what the server's default setting is via ##DATEFIRST. Or you could simply change it at the start of your query.
Some references:
MSDN
Similar Question
That worked for me like a charm:
Setting moday as first day of the week without changing DATEFIRST variable:
-- FirstDayWeek
select dateadd(dd,(datepart(dw, case datepart(dw, [yourDate]) when 1 then dateadd(dd,-1,[yourDate]) else [yourDate] end) * -1) + 2, case datepart(dw, [yourDate]) when 1 then dateadd(dd,-1,[yourDate]) else [yourDate] end) as FirstDayWeek;
-- LastDayWeek
select dateadd(dd, (case datepart(dw, [yourDate]) when 1 then datepart(dw, dateadd(dd,-1,[yourDate])) else datepart(dw, [yourDate]) end * -1) + 8, case datepart(dw, [yourDate]) when 1 then dateadd(dd,-1,[yourDate]) else [yourDate] end) as LastDayWeek;
Setting sunday as fist day of the week without changing DATEFIRST variable
select convert(varchar(50), dateadd(dd, (datepart(dw, [yourDate]) * -1) + 2, [yourDate]), 103) as FirstDayWeek, convert(varchar(50), dateadd(dd, (datepart(dw, [yourDate]) * -1) + 8, [yourDate]), 103) as LastDayWeek;
You can change [yourDate] by GETDATE() for testing
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'