Query all items for the week in SQL - sql-server

I have a query that selects all items for the given week and sets their date to be the start day of that week. In some cases the query sets an incorrect date value for the given item and I have traced the problem to be in either DATEDIFF or DATEADD functions.
The query is
SELECT DATEADD(WEEK, DATEDIFF(WEEK, 0, DateTimeValue), 0) AS NewDateTimeValue
Let's take a date March 27 2016 as an example. The DATEDIFF returns value of 6065 and the DATEADD with that value returns March 28 2016.
For the date March 26 2016, the DATEDIFF returns 6064 and the DATEADD March 21, 2016.
To me this sounds like a FirstDayOfWeek issue and that SQL Server thinks Sunday is the first day of a week and thus giving different values for Sunday and for Saturday (March 27, 2016 is Sunday).
I tried to set the first day of the week by
SET DATEFIRST 1
but that didn't make any difference and SQL returned the same results.
So what causes the SQL function to behave like this and any ideas how to fix it?

I found out that DATEDIFF doesn't respect the DATEFIRST setting and always assumes the week starts on Sunday. This is the reason why my sample case works as it works. This question has some suggested workaround: Is it possible to set start of week for T-SQL DATEDIFF function?

This is happening because you are finding the no. of weeks from date 0 to the supplied date. 0 is actually '1900-01-01' which was a Monday. Therefore,DATEDIFF finds the number of completed weeks from this date until the supplied data. Thats why DATEDIFF for March 27 2016 returns 6065 (as it is the end of a week) and March 26 2016 returns 6064 (as it is still not the end of a week).
This is already explained in this link - Get first day of the week

Related

Why SQL datepart(ww,GETDATE()) returns current week + 1

I am trying to get current week of the calendar. Let's assume today is 2022-03-14 12:00:00.
I am using these 2:
select GETDATE() // returns 2022-03-14 12:00:00
select datepart(ww,GETDATE() //returns 12
But as per calendar 2022-03-14 12:00:00 should be 11st week.
From where is the difference coming from and how I can get basically current week?
GetDatePart(ww, ...) in SQL server is dependent on the set datefirst as documented here.
You can use GetDatePart(isoww, getdate()) instead to get ISO Week Number.

SQL Week of Month without DATEFIRST

I am working with dates so I have created a function that generates a SQL Table Calendar which returns Day, Month, WeekOfMonth, WeekOfYear and so on.
Right now, for the Day of Month field I am using the following function:
-- [WkNo]=Week number
[WkNo] = DATEPART(week,dt.DT),
But the problem is that when I run this on a SQL installed with Language = US English, the week setting is wrong cause the week starts from Sunday.
I need to set the week starting from Monday, is it possible without the use of DATEPART?
Update: based on asker's comment that he cannot use the DATEFIRST approach I am updating answer.
Note:
This answer is generic in nature.
If instead of Monday you want the week to start from Tuesday, you can change the dateadd(dd,-1,dt.DT) to dateadd(dd,-2,dt.DT) and for Wednesday to dateadd(dd,-3,dt.DT).
Basically the formula becomes dateadd(dd,-n,dt.DT) for value of n ranging over 1(Monday) to 6 (Saturday).
SELECT [WkNo]=
ISNULL(DATEPART(week,
case
when Year(dt.DT)>YEAR(dateadd(dd,-1,dt.DT))
then null
else dateadd(dd,-1,dt.DT)
end
),1)
See working fiddle http://sqlfiddle.com/#!6/10a80/14
Old Answer:
See MSDN documentation: https://msdn.microsoft.com/en-us/library/ms174420.aspx
When datepart is week (wk, ww) or weekday (dw), the return value
depends on the value that is set by using SET DATEFIRST. January 1 of
any year defines the starting number for the week datepart, for
example: DATEPART (wk, 'Jan 1, xxxx') = 1, where xxxx is any year.
SET DATEFIRST 1
-- [WkNo]=Week number
[WkNo] = DATEPART(week,dt.DT),
try
SET DATEFIRST {1,2,3,4,5,6,7(default, U.S. English)}
for your Query:
set datefirst 1
select [WkNo]= DATEPART(week,dt.DT)
See Here

How do I use dateadd to get the first day of last year?

I'm trying to pull data that falls in between January 1st, 2014 and today's date last year (ie. August 3rd, 2014). How would I go about using ``dateadd` to get the date 1/1/14? I'm using the following code to get the date 8/3/14.
dateadd (yy, -1, getdate())
I want to avoid explicitly searching for 1/1/14 because in a year's time I'd like the sql query to find 1/1/15 without me having to go back in and rewrite it.
Use DATEFROMPARTS:
DATEFROMPARTS(YEAR(GETDATE()) - 1, 1, 1)
DATEADD(yy, DATEDIFF(yy,0,getdate())-1, 0)
DATEFROMPARTS() is the best way but it requires SQL2012 or later. If you're on an earlier vierion, try this:
You can use GETDATE() to get the current date
You can use the function YEAR() to extract the year from any date
Subtract 1 from it to get last year
Append 1/1/ to the front of it
Convert it back to a date again
select convert(datetime, '1/1/' + convert(varchar(max),year(getdate())-1))

Dynamic Date Range in SSRS or SQL Data Tools builder

I am making a report that will run on the 1st of every month and on the 16th of every month.
If it is running on the 1st I need the #start_date to be the 16th to the last day of the previous month.
If it is running on the 16th I need #start_date to be the 1st of the month through the 15th of the month.
I can think of a couple ways to do this, but I am curious is SSRS/Report Builder/SQL Data Tools builder has an easy method for setting this up.
My option was to make a SQL query that does what I need then plug that into the Get Balues from a query part of the parameter.
If you run the report in the first part of the month then the parameters should be set to the 16th to the end of the previous month; if run in the second part of the month then the parameters should be set to the 1st to the 15th of the current month.
#start_date Default Value expression:
=IIF(Day(Today) >= 16, DateAdd(DateInterval.Day, 1-Day(Today), Today), DateAdd(DateInterval.Month, -1, (DateAdd(DateInterval.Day, 16-Day(Today), Today))))
#end_date Default Value expression:
=IIF(Day(Today) >= 16, DateAdd(DateInterval.Day, 15-Day(Today), Today), DateAdd(DateInterval.Day, -1, (DateAdd(DateInterval.Day, 1-Day(Today), Today))))

SQL Server date calculations in stored procedures

I wanted to ask 3 questions about below code (please excuse the long code listing, I am including these lines in the hopes that it provides enough context).
Note that the code here depends on the date when it is executed. For this reason my questions refer to a hypothetical situation with two different execution dates:
March 1st 2014
January 1st 2014
My questions are on whether my understanding on some parts of this is correct, i.e:
A. that the SELECT DATEADD expression (on line 1) would:
On March 1st 2014 create the datetime 2014-02-31 23:59:59
B. that the code on lines 6-9 would:
On March 1st 2014 create the datetime 2014-02-01 00:00:00
On January 1st 2014 create the datetime 2013-02-01 00:00:00
and
C. that the code on lines 11-14 would:
On March 1st 2014 create the datetime 2015-01-30 00:00:00
On January 1st 2014 create the datetime 2014-01-30 00:00:00
Is this understanding correct?
1. SET #ldpmth = (SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE()),0)) )
2. SET #yr = (SELECT YEAR(#ldpmth))
3. SET #mth = 0
4. SET #dy = 0
5.
6. SET #fysdate = (SELECT CASE WHEN MONTH(#ldpmth) >= 2 THEN
7. DATEADD(MM, 1,CAST(CAST(#yr+#mth+#dy AS NVARCHAR(50)) AS DATETIME))
8. ELSE DATEADD(MM, 1, CAST(CAST((#yr-1)+#mth+#dy AS NVARCHAR(50))
9. AS DATETIME)) END )
10.
11. SET #fyedate = (SELECT CASE WHEN MONTH(#ldpmth) >= 2 THEN
12. DATEADD(YY, 1, CAST(CAST(#yr+#mth+#dy AS NVARCHAR(50)) AS DATETIME)) + 30
13. ELSE DATEADD(YY, 1, CAST(CAST((#yr-1)+#mth+#dy AS NVARCHAR(50))
14. AS DATETIME)) + 30 END )
(Thank you for all who answered so far. This is actually code that was developed (but not documented) at my place of employment some years ago and I have been tasked with converting it to a form that works client-side with Crystal Reports.)
On March 1st 2014 create the datetime 2014-02-31 23:59:59
No. AFAIK, there isn't any dbms that's so dumb it thinks Feb 31 is an actual date. The SQL statement on line 1 will return the value 'Feb 28 2014 23:59:59'.
You can test all your statements by substituting actual dates for GETDATE() and guesswork. In the first query, for example, use
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,'2014-03-01'),0));
^^^^^^^^^^
You can probably run a version of SQL Server locally. SQL Server 2008 even installs on Windows XP. If you can't bear that, though, there's always sqlfiddle.com.
This could be easily tested in SQL Server Management Studio (SSMS). I'll answer the first, and you can test the next two yourself.
Question:
A. that the DATEADD (on line 1) would on March 1st 2014 create the datetime 2014-02-31 23:59:59
Answer: No, because February can't possibly have 31 days, so it can't return '2014-02-31`
Proof:
select DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,'2014-03-01'),0))
Results
(No column name)
2014-02-28 23:59:59.000
For (B) and (C), if you'd stated your question, instead, as "My financial years run from 1st February until 30th(sic) January - how do I get the start and end dates for the current financial year, I'd have offered:
SET #fysdate = DATEADD(year,DATEDIFF(month,'19000201',GETDATE())/12,'19000201')
and:
SET #fyedate = DATEADD(year,DATEDIFF(month,'19000201',GETDATE())/12,'19010131')
(Note that this second finds the 31st Jan, not the 30th. I assume that was carelessness in your original question).
But, as to (A), as I indicated, I thought your question is again poorly phrased - if you're trying to find the end point of a period and that period is defined on datetimes (as opposed to just dates), it's far easier to construct an exclusive upper bound:
SET #ldpmth = DATEADD(month, DATEDIFF(month,0,GETDATE()),0)
This bound (to be used with a < comparison rather than <= or BETWEEN) doesn't artificially exclude any values with a time such as 23:59:59.527. Note also, that if the financial year end date calculation is going to be used against datetime values which include times, I'd again counsel computing an exclusive upper bound, and substitute 19010201 instead of 19010131.
So, in total, I don't think I've actually answered your questions as posed, but I think I've provided the answers you should have been seeking.

Resources