Confusion over the second argument of the DATEDIFF function - sql

I've referred to this on MSDN but I'm still unsure what the second argument in the DATEDIFF function is doing in the following two examples:
SELECT DATEDIFF(yy,0,getdate()) --run on 14 Aug this returns 112
SELECT DATEDIFF(yy,1000,getdate()) --I chose 1000 arbitrarily and run on 14 Aug this returns 110
Usually I'll use DATEDIFF to find the number of days, or number of years between two months and the second argument is then a date.
Reason I'd like to understand the above is to ultimately understand the following:
SELECT DATEADD(yy, DATEDIFF(yy,0,GETDATE()), 0)

If you use an integer as the second argument (or for any datetime/smalldatetime assignment for that matter), this is interpreted as the number of days since 1900-01-01.
DECLARE #d1 DATETIME = 0, #d2 DATETIME = 1;
SELECT #d1, #d2;
Result:
1900-01-01 00:00:00.000 1900-01-02 00:00:00.000
Note that this doesn't work for new data types like DATE during direct assignment:
DECLARE #d DATE = 0;
Result:
Msg 206, Level 16, State 2, Line 1
Operand type clash: int is incompatible with date
But it can still work using date math, e.g.:
DECLARE #d DATE = DATEADD(YEAR, 0, SYSDATETIME());
SELECT #d;
Result:
2012-08-14
For these inconsistent reasons, I recommend you use proper date literals so that it is clear which date you mean and so that it works regardless of the data type. This is a habit I find hard to break, since typing 0 is so much easier than 19000101...

Consider below example to understand this concept better. 0 is default date "1900-01-01"
Below Query gives output as 2017-10-31 00:00:00.000
SELECT DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH,0,SYSDATETIME())+3, 0))
Dividing the above query into various parts to understand the calculation.
-- 1414 Months since '1900-01-01'
SELECT DATEDIFF(MONTH,0,SYSDATETIME()) + 3
-- adding 1414 Months to Default date 1900 Produces '2017-11-01 00:00:00.000'
SELECT DATEADD(MONTH, DATEDIFF(MONTH,0,SYSDATETIME())+3, 0)
--Subtract 1 day from '2017-11-01 00:00:00:000' gives last day of previous month.
SELECT DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH,0,SYSDATETIME())+3, 0))

The second argument to datediff() is a date.
The first example returns the "start" date of time in the SQL Server world. That would be 112 years before the current date.
The second example is rather non-sensical. As implemented, the dates are represented as number days since the earliest date. This is the number of years since 1000 days after the earliest date.
The last example adds a number of years to the base date. It then adds a number of months. Since the base date is 1/1/1900, this is giving you the first date of the day after the nth month in the yth year.

Related

What does negative one mean when passed as a date in the DATEDIFF function?

I'm trying to understand the following T-SQL code:
select DATEADD(
MONTH,
DATEDIFF(
MONTH,
-1,
GETDATE()) -1,
-1)
What does -1 indicate when passing into DATEDIFF? According to this, it should be a date parameter.
Here, the -1 for the 2nd parameter for DATEDIFF, and the 3rd parameter for DATEADD will be implicitly converted to a datetime. You can find what the value would be with a simple expression:
SELECT CONVERT(datetime, -1);
Which gives 1899-12-31 00:00:00.000. For the "old" date and time data types ((small)datetime), they allow conversion from numerical data type (such as an int or decimal). 0 represents 1900-01-01 00:00:00 and each full integer value represents a day. So -1 is 1899-12-31 00:00:00.000, 2 would be 1900-01-03 00:00:00.000 and 5.5 would be 1900-01-06 12:00:00.000 (as
.5 represents 12 hours).
For the new date and time data types, this conversion does not exist.
In truth, the above could likely be much more easily written as the following though:
SELECT EOMONTH(GETDATE(),-1);
Here the -1 means the end of the month 1 month prior to the date of the first parameter (in this case the current date). The second parameter for EOMONTH is optional, so EOMONTH(GETDATE()) would return the last day of the current month, and EOMONTH(GETDATE(),2) would return the last day of the month in 2 months time (2023-03-31 at time of writing).

Calculate dates by giving a default day of month using SQL Server 2008

I need to calculate a future date by subtracting a date from another date. The catch is the date field I am subtracting has many days of the month...i.e. 10/27/2020 or 11/30/2020. What I need to do is, no matter what the day of the month is, I need to subtract the month as if it were the first day of the month no matter what it may be.
So for example I have a date 12/31/2020 I need to subtract another date 10/23/2020 from that date but I need to default 10/23/2020 to 10/01/2020 and same for any other date I would subtract from the original date. Any ideas?
You can get first day of the month and then use datediff to get the date difference.
DECLARE #table table(fromdate date,todate date)
insert into #table
VALUES ('2020-10-27','2020-12-31')
SELECT datediff(day, DATEADD(month, DATEDIFF(month, 0, fromdate), 0),todate) AS DifferenceDays
from #table
+----------------+
| DifferenceDays |
+----------------+
| 91 |
+----------------+
Not really sure what you are really asking. But I'm assuming that you want to get the date gap or difference between 2 dates. But, before that, you want to set a default day for the first date.
You can set up the first date to have day 1, then use DATEDIFF to know the gap / difference.
-- source date
declare #date date = '10/27/2020'
declare #dateSubtract date = '10/23/2020'
-- break the date, take month and year only
declare #month int = DATEPART(MONTH, #date)
declare #year int = DATEPART(YEAR, #date)
-- reconstruct the date with default day=1
select #date = CAST((STR(#month) + '/01/' + STR(#year)) as date)
-- get the calculation
select DATEDIFF(DAY, #date, #dateSubtract)
The result will be (in Days),
22
You can change the DATEDIFF parameter to MONTH or YEAR.
#UPDATE 1:
As mentioned by Alex in comment below, you can reconstruct the date using DATEFROMPARTS which more safer. Because casting from string may causing a confusion of date format.

Round a timestamp

I would like to differentiate between two dates and round the result to the next day.
For exemple, if I have date1='2020-03-10 11:59:00' and date2='2020-03-10 20:53:00', the difference between date1 and date2 with datediff() is equal to 8 hours. I would like to round this result to have 24 hours.
EDIT
I tried by using dateadd() like this :
select DATEADD(HOUR, DATEDIFF(HOUR, '2020-03-10 11:59:00', '2020-03-10 20:53:00'),0)
Return 1900-01-02 09:00:00.000 doesn't correspond to what I want.
The explanation of the output is not at all clear so I am just guessing here. I am taking the difference in days between two dates. Then adding 1 because if two dates are the same day the difference in days is 0. Then multiplying that result by 24. I changed up the date literal so it is ANSI compliant and will always get the correct date regardless of localization or language settings.
declare #date1 datetime = '20201003 11:59:00'
, #date2 datetime = '20201003 20:53:00'
select datediff(day, #date1, #date2) + 1 * 24

SQL datediff with negative parameter

I am a complete noob to SQL and trying to understand why this query returns "115":
select datediff(yy, -3, getdate())
datediff takes three Parameter. The first is the interval, the second the start date and the third the end date. You are passing -3 as start date, there we can show:
SELECT CAST(-3 AS datetime) -- results in '1899-12-29 00:00:00.000'
And because 2014 - 1899 is 115, you will get this as result.
Because DATEDIFF() calculates an interval between 2 dates, and you specified year -3 in one of them.
Firstly, the date "zero" is 12/30/1899 on SQL server.
Secondly, your "-3" was an incorrect date format and it thus replaced it with its 0
2014 - 1899 = 115
Use DATEADD() instead to achieve what you want to do.
DateDiff gives difference in years/date/months etc based on what you have given in first parameter. second and third parameter are datetime values which will be used to calculate the difference i.e. (param2 datetime value - param3 datetime value).
Now in your case param2 is "-3"
run this queries in you ms sql and observe the outputs :
select CAST(-3 as datetime)
select GETDATE()
select datediff(yy, -3, getdate())

Using Derived Column to create a date value in the format YYYY-MM-01 00:00:00.000 for each row

Im having a problem with derived columns in SSIS. When in SSMS i can set a column to have a default value using the following code:
(dateadd(month,datediff(month,(0),getdate())-(1),(0)))
and when data is entered into the database it will give it the timestamp of the previous month in the following format for example:
2010-09-01 00:00:00.000
This strangely is what im looking for and trying to duplicate/produce similar using the Derived Column DFT.
So far i have:
DATEADD("mm",DATEDIFF("mm",GETDATE(),GETDATE()) - 1,GETDATE())
which produces the month succesfully but the GETDATE() is not a correct replacement for the 0's in the original code.
Does the 0's in the original code signify the start date in SQL?
Any help would be much appreciated.
Regards,
Lee
your first code fragment is "flooring" the datetime to the month (making the date the 1st of the given month with no time) the "-1" makes it the previous month. All the extra unnecessary parenthesis in this code fragment give me a headache, here is the equivalent:
dateadd(month,datediff(month,0,getdate())-1,0)
This is how to floor a datetime to different units:
--Floor a datetime
DECLARE #datetime datetime;
SET #datetime = '2008-09-17 12:56:53.430';
SELECT '0 None', #datetime -- none 2008-09-17 12:56:53.430
UNION SELECT '1 Second',DATEADD(second,DATEDIFF(second,'2000-01-01',#datetime),'2000-01-01') -- Second: 2008-09-17 12:56:53.000
UNION SELECT '2 Minute',DATEADD(minute,DATEDIFF(minute,0,#datetime),0) -- Minute: 2008-09-17 12:56:00.000
UNION SELECT '3 Hour', DATEADD(hour,DATEDIFF(hour,0,#datetime),0) -- Hour: 2008-09-17 12:00:00.000
UNION SELECT '4 Day', DATEADD(day,DATEDIFF(day,0,#datetime),0) -- Day: 2008-09-17 00:00:00.000
UNION SELECT '5 Month', DATEADD(month,DATEDIFF(month,0,#datetime),0) -- Month: 2008-09-01 00:00:00.000
UNION SELECT '6 Year', DATEADD(year,DATEDIFF(year,0,#datetime),0) -- Year: 2008-01-01 00:00:00.000
ORDER BY 1
PRINT' '
PRINT 'Note that when you are flooring by the second, you will often get an arithmetic overflow if you use 0. So pick a known value that is guaranteed to be lower than the datetime you are attempting to floor'
PRINT 'this always uses a date less than the given date, so there will be no arithmetic overflow'
SELECT '1 Second',DATEADD(second,DATEDIFF(second,DATEADD(day,DATEDIFF(day,0,#datetime),0)-1,#datetime),DATEADD(day,DATEDIFF(day,0,#datetime),0)-1) -- Second: 2008-09-17 12:56:53.000
the second code fragment will not properly floor the datetime to the month, it will only move the date to the previous month and use the same day and time as the given datetime.
Beyond that I'm not sure what you are really asking.
here is a breakdown of what is happening in the OP's first code fragment:
select convert(datetime,0),'first datetime "0"'
select datediff(month,0,getdate()), 'months difference between the first datetime (1900-01-01) and given "getdate()"'
select datediff(month,0,getdate())-1, 'months difference between the first datetime (1900-01-01) and month previous to given "getdate()"'
select dateadd(month,datediff(month,0,getdate())-1,0), 'takes the first datetime (1900-01-01) and adds 1328 months onto that'
OUTPUT:
----------------------- ------------------
1900-01-01 00:00:00.000 first datetime "0"
----------- --------------------------------------------------------------------------------
1329 months difference between the first datetime (1900-01-01) and given "getdate()"
----------- --------------------------------------------------------------------------------------------------
1328 months difference between the first datetime (1900-01-01) and month previous to given "getdate()"
----------------------- --------------------------------------------------------------------
2010-09-01 00:00:00.000 takes the first datetime (1900-01-01) and adds 1328 months onto that
Here's what I think you may be looking for. It is an SSIS expression that gets the first day of the previous month for a given day (GETDATE() in the example).
DATEADD("mm",DATEDIFF("mm", (DT_DBTIMESTAMP)2, GETDATE()) - 1, (DT_DBTIMESTAMP)2)
I tried to simulate your SQL version of the expression, which determined the number of months between the 0 datetime and the current datetime. And, then it added the number of months to the 0 datetime.
It's not too important what the 0 datetime is, except that you wanted the 1st day of the month. In SQL the 0 datetime is 1900-01-01 00:00:00.000, so adding months automatically gives you the first day of the month. In SSIS expressions, the 0 datetime is 1899-12-30 00:00:00.000. Since you want the first day of a month, the expression above refers to the 2 datetime. So, in the expression (DT_DBTIMESTAMP)2 casts the number 2 to 1900-01-01 00:00:00.000.
month,0 makes it round down to the beginning of the month.
Can't you use your first expression if it does what you want? Or use the second expression but switch the two GETDATE()s you've added to 0. Do you get an error if you try that?
I think what you need in your derived column is this synatx:
DATEADD("mm",-1,(DT_DBTIMESTAMP)((DT_WSTR,4)DATEPART("YYYY",GETDATE()) + "-" + (DT_WSTR,2)DATEPART("mm",GETDATE()) + "-01"))