I have the following simple query
select DATEADD(MONTH, DATEDIFF(MONTH, -32, '2020-02-29')-32, -1)
select DATEADD(MONTH, DATEDIFF(MONTH, -31, '2020-02-29')-31, -1)
I would expect the output for each line would be different due to the differing number of months in -31 and -32.
Both of these lines return 2017-07-31 00:00:00.000 and I have absolutely no idea why!
Can somebody explain this to me?
As explained in the documentation for dateadd():
If the following are true:
datepart is month
the date month has more days than the return month
the date day does not exist in the return month
Then, DATEADD returns the last day of the return month.
From my comment:
Why would you expect different values? Both DATEDIFF(MONTH, -32, '20200229')-32 and DATEDIFF(MONTH, -31, '20200229')-31 result in the value 1411. The "date" -1 is 1899-12-31, and adding 1411 months (117 years 7 months) to that is 2017-07-31.
Let's break it down:
SELECT DATEDIFF(MONTH, -32, '20200229')-32 AS DD1, DATEDIFF(MONTH, -31, '20200229')-31 AS DD2;
This returns the below:
DD1 DD2
----------- -----------
1411 1411
We can break the above into further steps too. For a datetime, the date 0 is 1900-01-01 and every full integer represents adding that many days to that date. -32 as a date is therefore 1899-11-30, and -31 is 1899-12-01. This gives us the 2 below expressions:
SELECT DATEDIFF(MONTH, '18991130', '20200229') AS M1, DATEDIFF(MONTH, '18991201', '20200229') AS M2;
This returns the below:
M1 M2
----------- -----------
1443 1442
This make sense, as DATEDIFF counts the number of "ticks" between 2 dates. So for the expression DATEDIFF(YEAR, '2019-12-31T23:59:59.9999999','2020-01-01T00:00:00.000000') the value 1 is returned, even though only 1 millisecond has passed, because the value of the the year has changed (by 1).
The next part of that expression is 1443 - 32 and 1442 - 31 respectively. This is 1411 for both (basic maths).
You then have the "date" -1. You then add 1411 months (which is 117 years and 7 months) to the date 1899-12-31, which (unsurprisingly) returns the same value: 2017-07-31. 1899 + 117 = 2016. 12 + 7 = 7 because we're working in months, so carry the 1 to the year: 2017-07-31.
Related
Using below depart syntax to fetch for previous month record, it is working fine till previous year,however it is giving 0 value in January month.How can we get pervious month with date part even if year is change ?
DATEPART(month(GETDATE()) -1
I understand that I used another type of DB, but I want to give a hint. I am using sql server 2019.
Firstly, you need to substitute date and only then take datepart from it.
Queries:
--dateadd -1 would subtract 1 from current month
--(Jan - 1 2022), would be December 2021
select datepart(month, dateadd(month, -1, getdate()))
--also date add covers internally the problem with 30,31 days.
--May always has 31 days, April 30. So -1 subtraction from 31th of May,would result in 30th of April.
select dateadd(month, -1, cast('2021-05-31 10:00:00' as datetime))
I'm trying to tally up the number of unique ID accounts that are active for more than a year as well as include how long each one is active. The problem with the code I have is it's not including accounts that are currently active (ones that don't have an end date). For example, if the begin date was May 01 2018 but has no end date since it's currently active, it should also be included in this query. Here's what I have so far..
SELECT UniqueID,
DATEDIFF(yy, Begin_Date,End_Date) as timeactive
FROM TABLE
WHERE DATEDIFF(yy, Begin_Date,End_Date) > 1
I want my output to look like...
Unique ID Time active
F000012 2.5
F000031 1.5
This is what ended up working:
SELECT UniqueID,
CAST(ROUND(DATEDIFF(day, Begin_Date, COALESCE(End_Date, getdate()))/365.0, 1, 0) AS NUMERIC (10,1)) as timeactive
FROM TABLE
WHERE DATEDIFF(day, Begin_Date, COALESCE (End_Date, getdate())) >= 365
If the EndDate is null then the output of the DateDiff function will be null, and any null compared to anything (even another null) is a result of null (usually then interpreted as false)
I suggest you use COALESCE to convert your end date to today if it is null:
SELECT
UniqueID,
DATEDIFF(yy, Begin_Date,COALESCE(End_Date, GetUtcDate()) as timeactive
FROM TABLE
WHERE DATEDIFF(yy, Begin_Date,COALESCE(End_Date, GetUtcDate()) > 1
You should bear in mind that the DATEDIFF function as used here, in SQLserver does NOT return the amount of time that has passed between the two dates. It returns the number of times the named interval has changed between the two dates
For example, DATEDIFF(year, 2000-01-01, 2000-12-31 23:59:59) will return 0 because these are both year 2000 even though they're just one second short of being a year apart. If you do DATEDIFF(year, 2000-12-31 23:59:59, 2001-01-01 00:00:01) even though these dates are only two seconds apart datediff will report them as 1 year apart because the year number has changed from 2000 to 2001.
DATEDIFF counts up by one every time the clock rolls past an interval change and in this case the interval is Year
To get your dates to report as 1.5 years etc you should consider to datediff by a smaller interval and divide, such as asking for the DAYS diff between two dates and then dividing by 365.25- the average number of days in a year. The smaller the interval you ask datediff for the more accurate the result will be but it'll never be 100%. If you're only after results to one decimal place of a year then days will be accurate enough
To get 1 decimal place, cast to a numeric with 1 DP:
SELECT
UniqueID,
CAST(DATEDIFF(day, Begin_Date,COALESCE(End_Date, GetUtcDate())/365.25 AS NUMERIC(5,1)) as timeactive
FROM TABLE
WHERE DATEDIFF(day, Begin_Date,COALESCE(End_Date, GetUtcDate()) >= 365
If you want time active as fractional years, then you need to use a smaller unit of time and divide. For instance:
SELECT UniqueID,
DATEDIFF(month, Begin_Date, COALESCE(End_Date, GETDATE())) / 12.0 as timeactive
FROM TABLE
WHERE Begin_Date < DATEADD(YEAR, -1, COALESCE(End_Date, GETDATE()))
Note the change in the WHERE clause. DATEDIFF() counts the number of year boundaries between dates. So the difference in years between 2019-01-01 and 2020-12-31 is the same as the difference between 2019-12-31 and 2020-01-01 -- exactly 1.
Consider:
SELECT
UniqueID,
DATEDIFF(yy, Begin_Date, COALESCE(End_Date, getdate()) as timeactive
FROM TABLE
WHERE DATEDIFF(yy, Begin_Date, COALESCE(End_Date, getdate()) > 1
This works by using the current date as default value for empty End_Dates. So this allows records with empty end date if their start date is more than one year ago.
I have a table that users enter a daily population. How many people in a particular facility that day. The table looks similar to this:
select * from stat_summary where MONTH(report_date) = 9
results:
stat_summary_id | report_date | facility | adp
----------------------------------------------------
29 |2015-09-01 | YORK | 1855
30 |2015-09-02 | YORK | 1750
31 |2015-09-04 | YORK | 1655
32 |2015-09-04 | YORK | 1699
What I want to do is calculate the average daily population grouped by month. I want to take the MAX(report_date) in case a corrected value has to be re-entered. My query looks like:
SELECT
MONTH(t.report_date) as 'report_month',
SUM(ss1.adp)/DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,MONTH(t.report_date)),0))),
DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,MONTH(t.report_date)),0)))
FROM
stat_summary ss1
INNER JOIN
(SELECT MAX(stat_summary_id) as 'stat_summary_id', report_date
FROM stat_summary
GROUP BY report_date
) t ON t.stat_summary_id = ss1.stat_summary_id
WHERE
ss1.facility_id = 'YORK'
AND MONTH(t.report_date) = 9
GROUP BY
MONTH(t.report_date)
ORDER BY
MONTH(t.report_date)
I've referenced this thread:
Dividing a value by number of days in a month in a date field in SQL table
And I was able to see how to dynamically divide by the number of days in the month, but it looks like it is dividing by the current month (October) which has 31 days, when I need the query to divide by the referenced month of September which has 30 days.
Currently my results look like:
The adp value should be 176.8 since there are 30 days in September, not 31.
So quick check it looks like that formula returns 31 for all months. The proper formula can be found here: How to determine the number of days in a month in SQL Server?
datediff(day, dateadd(day, 1-day(#date), #date),
dateadd(month, 1, dateadd(day, 1-day(#date), #date)))
More precisely use:
datediff(day, dateadd(day, 1-day(MIN(t.report_date)), MIN(t.report_date)),
dateadd(month, 1, dateadd(day, 1-day(MIN(t.report_date)), MIN(t.report_date))))
EDIT: Note the original formula was in fact correct, the problem was that you were passing in a month instead of a day. Months are numbers from 1-12, so all of your dates were in January.
I should use
day(DateAdd(day, DateAdd(month, MONTH(t.report_date), DateAdd(Year, YEAR(t.report_date)-1900, 0)), -1)) as monthDays
Also sounds to me that to obtain average, it is wrong to divide by that number, it is only correct if the number of records match the number of days in the month, in other case, just the count is enough
SUM(ss1.adp)/count(ss1.adp) as average
Datediff is very confusing for fetch the difference between two days:
DATEDIFF(DAY, '2014-09-01','2014-09-07')+1) AS totaldays
when i write above then it will give me 7 days it is perfect....
but when i write the below
DATEDIFF(DAY, '2014-09-01','2014-09-02')+1) AS totaldays
then it will give me 2 days but i want 1 day with this below function
DATEDIFF(DAY, '2014-09-01','2014-09-02')+1) AS totaldays
how can i get it?
The DATEDIFF function is working as expected.
DATEDIFF simply subtracts the second parameter from the third parameter by the specific element specified in the first parameter.
For example, the following query provides four columns... all which subtract the day specified in the second parameter from the day specified in the third:
Select DATEDIFF(DAY, '2014-09-01','2014-09-07')+1, -- Calculation: (7-1)+1 = 7
DATEDIFF(DAY, '2014-09-01','2014-09-07'), -- Calculation: (7-1) = 6
DATEDIFF(DAY, '2014-09-01','2014-09-02')+1, -- Calculation: (2-1)+1 = 2
DATEDIFF(DAY, '2014-09-01','2014-09-02') -- Calculation: (2-1) = 1
And the results are as expected:
Col1 | Col2 | Col3 | Col4
7 | 6 | 2 | 1
Here is the MS documentation for this function.
If you want the function to act differently then you will need to utilize CASE statement.
A "day" has a "duration" (that can be measured in units such as 24 hours)
That duration commences at 00:00:00 +0000
When we write a date/time as '2014-09-07' the time is assumed to be
00:00:00 +0000
so this: DATEDIFF(DAY, '2014-09-01','2014-09-07')
is measuring a duration between: "the start of 2014-09-01" to "the start of 2014-09-07" and; trying to represent this as a set of durations where "S" indicates the start point and "---" is the countable duration.
1 2 3 4 5 6 7 (day of the month)
S---S---S---S---S---S---S (time span)
Count the number of "---" in that time span (6) [and the number of start points, "S", is 7]
DATEDIFF() is measuring "duration" NOT the number of start points
What you need to do is add one day to the higher date instead of adding one to the result, because you are using "start of" not "end of" points of time.
from to
start of start of
DATEDIFF(DAY, '2014-09-01','2014-09-08') = 7
DATEDIFF(DAY, '2014-09-01','2014-09-07') = 6
DATEDIFF(DAY, '2014-09-01','2014-09-06') = 5
DATEDIFF(DAY, '2014-09-01','2014-09-05') = 4
DATEDIFF(DAY, '2014-09-01','2014-09-04') = 3
DATEDIFF(DAY, '2014-09-01','2014-09-03') = 2
DATEDIFF(DAY, '2014-09-01','2014-09-02') = 1
DATEDIFF(DAY, '2014-09-01','2014-09-01') = 0
Remove +1 from your SQL.
Use the following : DATEDIFF(DAY, '2014-09-01','2014-09-02') AS totaldays
Due to +1 it was giving wrong output.
I have a column in my sql server 2005 table that should hold the number of months an employee has been in service.
Since I also have the date the employee was engaged, I want the "months_In_Service" column to be a computed column.
Now if I use DATEDIFF(month,[DateEngaged],GETDATE()) as the formula for the months in service computed column, the results are correct some times and other times incorrect.
What would be the better reliable way to get the number of months between the DateEngaged value and the current date? Which formula should i use in my computed column?
Something like (might need to swap the 1 and 0, untested)
datediff(month,[DateEngaged],getdate()) +
CASE WHEN DATEPART(day, [DateEngaged]) < DATEPART(day, getdate()) THEN 1 ELSE 0 END
DATEDIFF measure month boundaries eg 00:00 time on 1st of each month, not day-of-month anniversaries
Edit: after seeing OP's comment, you have to subtract 1 if the start day > end day
DATEDIFF (month, DateEngaged, getdate()) -
CASE
WHEN DATEPART(day, DateEngaged) > DATEPART(day, getdate()) THEN 1 ELSE 0
END
So for 20 Dec to 13 Jan, DATEDIFF gives 1 and then 20 > 13 so subtract 1 = zero months.
Same approach as gbn, but with less keystrokes :-)
SELECT
DATEDIFF(MONTH, DateEngaged, GETDATE()) +
CASE
WHEN DAY(DateEngaged) < DAY(GETDATE())
THEN 1
ELSE 0
END
Maybe you want something like:
(year(getdate())-year([DateEngaged]))*12+(month(getdate())-month([DateEngaged]))
If You presume that month is meaning for 30 days You can also round vale
round((datediff(day,[DateEngaged],getdate()))/30.00,0)