What is the difference between date handling in my queries? - sql-server

I need the number of records of last month. I used to use this approach so far:
select
count(ID)
from
table
where
col1 = 'somerule'
and
DateTimeOfInsert >= '20150901'
and
DateTimeOfInsert <= '20150930'
Now I'm about to atomate this task and therefore I have to pull the start and end dates of last month automatically. So here it is:
select
count(ID)
from
table
where
col1 = 'somerule'
and
DATEPART(m, DateTimeOfInsert) = DATEPART(m, DATEADD(m, -1, getdate()))
and
DATEPART(yyyy, DateTimeOfInsert) = DATEPART(yyyy, DATEADD(m, -1, getdate()))
My only issue is that at this very moment the first query returns 1073 and the second one 1124. So the question is obvious: what is the difference between them? Both should inlude the start and end date. I can't spot it.

This condition:
DateTimeOfInsert >= '20150901' and DateTimeOfInsert <= '20150930'
retrieves record that are between 2015-09-01 00:00:00.000 and 2015-09-30 00:00:00.000.
If DateTimeOfInsert is DATETIME, then this will return different result from your other condition.
The best way for this kind of queries is not to use BETWEEN but rather use
>= and <. In your case, you want to get records for the last month, so you want to use >= start of last month and < start of this month:
DateTimeOfInsert >= DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 1, 0) -- Beginning of previous month
AND DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) -- Beginning of this month
The above condition also makes your query sargable.
For some common date routines, see this article.

The difference is in time component of datetime.
This:
DateTimeOfInsert >= '20150901' and DateTimeOfInsert <= '20150930'
will not select date like 20150930 15:30.
But this:
DATEPART(m, DateTimeOfInsert) = DATEPART(m, DATEADD(m, -1, getdate()))
will select it because you are checking for months and year part only. That`s why the second select returns more rows.
Both queries will return the same if you just change the first statement so that it will consider time component of last day of month:
DateTimeOfInsert >= '20150901' and DateTimeOfInsert < '20151001'

Related

How to find the past 4 weeks of the same weekday value starting today

I am trying to select records from today and the same day of each week for the last 4 weeks.
Today (Tuesday)
Last Tuesday
The Tuesday before that
The Tuesday before that
I need this to be tied to current date because I am going to run this query every day so I don't want to use a between or something where I manually specify the date range.
Everything I have found or tried so far has pulled the last month of data but not the last 4 weeks of the same weekday.
select *
from table
where thedatecolumn >= DATEADD(mm, -1, GETDATE())
This works but pulls everything from the last month.
If today's date is 7/10/2019 I need
Data from 7/10/2019
Data from 7/3/2019
Data from 6/26/2019
Data from 6/19/2019
Every day I will run this query, so I need it to be dynamic based on the current date.
I believe you want to look back 21 days and then filter those dates that have the same day of week:
select * from table
where thedatecolumn >= DATEADD(DAY, -21, CAST(GETDATE() AS DATE))
and DATEPART(WEEKDAY, thedatecolumn) = DATEPART(WEEKDAY, GETDATE())
You Can try using a recursive cte which starts today and repeatedly substracts 7 days - so you ensure you always land on the same weekday. Following an example:
WITH cteFromToday AS(
SELECT 0 AS WeeksBack, GETDATE() AS MyDate
UNION ALL
SELECT WeeksBack + 1 AS WeeksBack, DATEADD(d, -7, MyDate) AS MyDate
FROM cteFromToday
)
SELECT TOP 5 *
FROM cteFromToday
OPTION ( MaxRecursion 0 );
This is quite simple. Substitute CURRENT_TIMESTAMP here for any given date.
SELECT CONVERT(DATE,CURRENT_TIMESTAMP) AS Today,
DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)) AS LastWeek ,
DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)) AS TwoWeeksAgo,
DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP)) AS ThreeWeeksAgo
SO, if you want to get data for a set of ranges for one entire day with those dates:
SELECT something
WHERE
datetimecolumn >= CONVERT(DATE,CURRENT_TIMESTAMP) AND datetimecolumn < DATEADD(DAY,1, CONVERT(DATE,CURRENT_TIMESTAMP)) -- Todays range,
OR datetimecolumn >= DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1,DATEADD(DAY,-7,CONVERT(DATE,CURRENT_TIMESTAMP)))-- LastWeek ,
OR datetimecolumn >= DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1,DATEADD(DAY,-14,CONVERT(DATE,CURRENT_TIMESTAMP)))-- TwoWeeksAgo,
OR datetimecolumn >= DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP)) AND datetimecolumn < DATEADD(DAY,1, DATEADD(DAY,-21,CONVERT(DATE,CURRENT_TIMESTAMP))) -- ThreeWeeksAgo

Get results for one day back in sql server

I am trying to get results from 1 day back, for example if i have a job that runs today at 1:00:00 am the 22/05/2018 i want it to get back the results for the 21/05/2018 00:00:00 am to 21/05/2018 23:59:59 pm.
i tried the follwing
select *
from table
where CreatedDateTime BETWEEN DATEADD(day, -1, GETDATE()) AND DATEADD(day, -0, GETDATE()) // it brings back everything from yesterday and today
example of how my created date time is stored in the db 2018-05-21 16:39:09.4830000
The bewteen operator filters the dates based on >= and <=
You need :
select *
from table
where CreatedDateTime >= DATEADD(day, -1, GETDATE()) AND
CreatedDateTime < GETDATE();
I suspect you would need cast(... as date) if so, the you can directly express this as
select *
from table
where cast(CreatedDateTime as date) = cast(DATEADD(day, -1, GETDATE()) as date);
Here is a good BLOG on filtering date range in query.
SELECT *
FROM table
WHERE CreatedDateTime BETWEEN GETDATE() -1 AND GETDATE()

Counting datediff from 2 date

I have 2 datetime fields, NEW_EMPLOYMENT_STARTDATE and NEW_EMPLOYMENT_ENDDATE
When I calculate the difference in months using datediff with these values for startdate and enddate:
NEW_EMPLOYMENT_STARTDATE = 2017-15-01
NEW_EMPLOYMENT_ENDDATE = 2018-14-01
With this query:
DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, NEW_EMPLOYMENT_ENDDATE)
It returns 12 months, but when I have values like this:
NEW_EMPLOYMENT_STARTDATE = 2017-01-01
NEW_EMPLOYMENT_ENDDATE = 2017-31-12
It returns 11 months.
How can I get 12 months? I am using this query:
DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, NEW_EMPLOYMENT_ENDDATE)-
CASE
WHEN DATEPART(DAY, NEW_EMPLOYMENT_ENDDATE) > DATEPART(DAY, NEW_EMPLOYMENT_STARTDATE)
THEN 0
ELSE 0 END AS MONTH_DIFF
It still returns 11 months.
EDIT:
According to my case, the value of the end date always less 1 day from start date, so i make a trick to check condition with case when like this:
CASE
WHEN DATEPART(DAY, NEW_EMPLOYMENT_ENDDATE) > DATEPART(DAY, NEW_EMPLOYMENT_STARTDATE)
THEN DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, NEW_EMPLOYMENT_ENDDATE)+1
ELSE DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, NEW_EMPLOYMENT_ENDDATE)
END AS DATEDIF
i add + 1 value to the end date so i ta can be round to next month, give feedback from my solution sir thanks
You're expectations are incorrect. When you do a DATEDIFF using MONTH, it does not consider the day portion of the dates. Consider that it is simply considering the difference in the month numbers only, regardless of the day specified.
This query:
SELECT DATEDIFF(MONTH, '20170101', '20171231') MonthsDiff
Is equivalent to this:
SELECT DATEPART(MONTH, '20171231') - DATEPART(MONTH, '20170101') MonthsDiff
The documentation for DATEDIFF states:
DATEDIFF ( datepart , startdate , enddate )
The first option is DATEPART:
datepart
Is the part of startdate and enddate that specifies the type of boundary crossed.
If you want something closer to what you expect, you can do a simple calculation based on performing the DATEDIFF in days, the dividing it by the approximate number of days in a month.
SELECT DATEDIFF(DAY, '20170101', '20171231') / ( 365 / 12 ) MonthsDiff
This will round the output to the closest month number, it all depends on how accurate you want to be. If you want months as a decimal for greater accuracy then run the below:
SELECT DATEDIFF(DAY, '20170101', '20171220') / ( 365.00 / 12 ) MonthsDiff
Note: This does not take into account leap years, for larger date ranges that might include leap years, which will make a minor difference to the accuracy.
datediff() does something very particular. It counts the number of "boundaries" between two date/time values. In your case, there are eleven boundaries -- one for each month in the year before December.
This behavior is not necessarily intuitive. If you add one day to each of your dates:
NEW_EMPLOYMENT_STARTDATE = '2017-01-02' (YYYY-MM-DD is standard format)
NEW_EMPLOYMENT_ENDDATE = '2018-01-01'
Then you will have 12 months.
If you want to round up, you can play with the dates. One method would be to normalize the first value to the beginning of the month and then add 15 days to the second value:
DATEDIFF(MONTH,
DATEADD(DAY, 1 - DAY(NEW_EMPLOYMENT_STARTDATE), NEW_EMPLOYMENT_STARTDATE)
DATEADD(DAY, 15 + 1 - DAY(NEW_EMPLOYMENT_STARTDATE), NEW_EMPLOYMENT_ENDDATE)
)
This would happen to work for the two examples you give.
Please use this select to achieve your desired result. You can use table columns instead of variables:
declare #new_employment_startdate datetime = convert (datetime, '2017-01-01', 121);
declare #new_employment_enddate datetime = convert (datetime, '2018-01-14', 121);
select
datediff(month, #new_employment_startdate, #new_employment_enddate)
+ case when
datediff(month, dateadd(ms, -3, dateadd(dd, datediff(dd, 0, #new_employment_startdate), 0)), #new_employment_startdate) = 1
and datediff(month,#new_employment_enddate , dateadd(dd, datediff(dd, 0, #new_employment_enddate) + 1, 0)) = 1
then 1
else 0
end;
Some explanations:
I check or start date is first month day AND end date is last month day. At this case I add +1 to standard datediff by month.
You can better understand my used datetime manipulations by using these example queries: https://gist.github.com/runnerlt/60636b029ab47845fdfd8924ed482e61
You need to add 1 more day in your End Date.
DATEDIFF(MONTH, NEW_EMPLOYMENT_STARTDATE, DATEADD(DD,1,NEW_EMPLOYMENT_ENDDATE))
You could match the output with MS Excel.

SQL syntax for Same Date Last Year

I am using SQL Server 2014 and I have the following T-SQL query which pulls data from a View Table:
SELECT * from MyView
WHERE StayDate >= '2014-07-01'
I need to add a filter to this query that will be applied on a field called "CreatedOn" (it is datetime field). The filter needs to filter the "CreatedOn" based on the SAME DATE LAST YEAR.
Thus, if today's date is '2015-10-26', my query should look this:
SELECT * from MyView
WHERE StayDate >= '2014-07-01'
AND CreatedOn <= '2014-10-26'
Since this query will be used in a PowerPivot environment, I am thinking along these lines:
SELECT * from MyView
WHERE StayDate >= '2014-07-01'
AND CreatedOn <= getdate()
How do I modify the getdate() part so that it becomes the Same Date Last Year?
To get the date a year before the current date, you could use:
DATEADD(YEAR, -1, GETDATE())
However, since that includes the time component, there's a possibility that some records will be left out. You should use this instead:
< DATEADD(DAY, 1, DATEADD(YEAR, -1, DATEDIFF(DAY, '19000101', GETDATE())))
The above will return the date a year before the current plus one day. That is, if today's date is '2015-10-26', the above will return '2014-10-27'. Note that this will be without a time component and you should be using < for the comparison.
More common date routines.
SELECT getdate() - 365
i.e,
SELECT * from MyView
WHERE StayDate >= '2014-07-01'
AND CreatedOn <= getdate() - 365
Edit: Not applicable for leap year. Thank you #mechnicov.

TSQL retrieve all records in current month/year

I have a datetime field called DateFinished. I need to be able to retrieve all records in which DateFinished is within the current month/year.
If you've only got a small number of rows, this will do to get all rows where DateFinished is in this month of this year.
SELECT *
FROM MyTable
WHERE Year(DateFinished) = Year(CURRENT_TIMESTAMP)
AND Month(DateFinished) = Month(CURRENT_TIMESTAMP)
This could get quite slow over a large number of rows though - in which case using DateAdd, DatePart and BETWEEN is probably more appropriate, and can take advantage of indexes (I don't have time to write an answer involving those right now!)
Just as an alternative - this should use an index on DateFinished.
SELECT *
FROM MyTable
WHERE DateFinished BETWEEN
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
AND
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0)
So the problem with #Bridge's method is use of index.
#Moose & #PCurd's method has a problems depending on how the data is stored.
#PCurd's method would work fine if all data collected on a day is rounded down to that day. E.g. event at 5pm is recorded as 2021-11-30 00:00:00. But if time is kept (which is assumed as it is a datetime field in Ops situation) then this data will be lost.
So you need to use the <> operators.
SELECT *
FROM MyTable
WHERE DateFinished >=
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)
AND DateFinished <
DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0)
For the method using datefromparts: SQL select records with current month

Resources