T-SQL Date function with Integer values as date parameter - sql-server

I was trying to modify a legacy query and come across this code block
select DATEADD(MONTH, DATEDIFF(MONTH, -1,getdate() )-1, 1)
But in my belief the dateadd and datediff function needs some date parameter to do the calculation. But here the DateDiff and DateAdd has some integer values at the place of Date parameter
For example DATEDIFF(MONTH, -1,getdate() )-1 returns 1431 which is just a decimal value, but the code block according documentation looks for Date parameter.
https://www.w3schools.com/sql/func_sqlserver_datediff.asp
Same in the case of DateAdd as well
Someone please share some explanation

Unfortunately, DATEADD and DATEDIFF are specified to work with datetime (as well as any of the newer datetime datatypes). As such, they inherit a lot of the nastiness of the old type, including that implicit conversions from int to datetime are allowed1.
0 converts to 1900-01-01. -1 converts to 1899-12-31. 1 converts to 1900-01-02. In short, the integer is converted to the day "that many" days after 1900-01-01.
But lets look at your code. It calculates the difference in months from 1899-12-31 and then, having subtracted one, adds that same number of months onto 1900-01-02. The net effect of this is to give you the 2nd of whatever the current month is.
There are simpler ways to write this even keeping the same structure:
select DATEADD(MONTH, DATEDIFF(MONTH, 0, getdate() ), 1)
Or using DATEFROMPARTS, which is preferred because it makes it explicit/obvious what you're doing and uses a more modern datatype (date).
1You're not even allowed explicit conversions from int to datetime2. Because, really, it doesn't make sense.

Related

Date between two dates giving zero counts

I want to extract records where ActionOwnerDate is between two specific dates therefore i do:
SELECT count(*)
FROM T_Warehouse
WHERE CAST(ActionOwnerDate As DATE) BETWEEN CAST('12.01.2020 11:21:08' As Date) AND CAST('12.10.2020 11:21:08' As Date);
Nevertheless data is available and should be shown:
What i get is 0 counts. Why is that?
So the better question is why you choose to do all this casting in the first place. Had you not chosen that path, your code would be working correctly. Was this choice just a kludge to avoid writing proper logic using datetime values? That's my guess.
So don't cast and use lower and upper boundaries that are appropriate for your datatype and goal. For datetime ranges, it is preferred to use an inclusive lower boundary and an exclusive upper boundary (due to the precision of the datatype - no one wants to use a literal time component of 23:59:59.997 ).
SELECT count(*)
FROM dbo.T_Warehouse as wh
WHERE wh.ActionOwnerDate >= '20200112'
AND wh.ActionOwnerDate < '20201013';
Notice that I guessed which format you assume in your literals based on your "doesn't work" implication and changed them to be unambiguous. I could have chosen Dec 1 through Dec 10 which is common for US.
When you try to convert 12.01.2020 11:21:08 as date it converted to 01-Dec-2020 and 12.10.2020 11:21:08 converted into 10-Dec-2020 and as you shown your table haven't contain data for between these dates.
If You want to retrieve data between 12-Jan-2020 and 12-Oct-2020 then use below mention query:-
Use DD-MMM-YYYY for better understanding.
SELECT count(1) FROM T_Warehouse WHERE CAST(ActionOwnerDate As DATE) BETWEEN '12-Jan-2020' AND '12-Oct-2020';

Why does ISDATE(YEAR(GETDATE())) return 1?

Why does ISDATE(YEAR(GETDATE())) or ISDATE(2020) or ISDATE(<any four digit number>) or ISDATE(<any expression that returns a four digit number>) return a 1?
With all these returning a 1, can we really rely on ISDATE() to know if an expression is a date expression?
Here's why I'm concerned:
SELECT ISDATE(4000-2000) returns a 1 although it's not a date expression.
Because ISDATE (like ISNUEMRIC) isn't a great function. ISDATE was designed with the older date and time data types (datetime and smalldatetime) in mind, which means that a int can be implicitly converted to Date and Time. This is not true for the new Date and Time datatypes (such as datetime2 and date).
For the older data types, an int value represents that many days after 1900-01-01. So 0 is 1900-01-01, and 2 would be 1900-01-03. 2020 would therefore be 1905-07-14.
If you truly want to see if a value is a valid date and time value, for the data type you are using, use TRY_CONVERT. For example TRY_CONVERT(date,2020) will return NULL, as 2020 cannot be converted. On the other hand TRY_CONVERT(datetime,2020) will return 1905-07-14 as it can be converted; even if the value isn't what you might expect.
Additional Note: 4 digit strings can also be implicitly converted. a 4 digit string would represent the year of the date only, and therefore '2000' would represent 2000-01-01. This is true for both the older date and time data types, and the new ones. Both TRY_CONVERT(date,'2020') and TRY_CONVERT(datetime,'2020') will return 2020-01-01.
As Martin Smith mentions, the above is note actually more relevant to what is happening here. As per ISDATE (Transact-SQL) the parameter for ISDATE is:
Is a character string or expression that can be converted to a character string. The expression must be less than 4,000 characters. Date and time data types, except datetime and smalldatetime, are not allowed as the argument for ISDATE.
This means that the expression ISDATE(2020) and ISDATE('2020') are synonyms. This is actually really bad, as 2020 cannot be converted to a date (implicitly or explicitly), but can be to a datetime . This further (in my opinion) cements that ISDATE should be avoided. Considering that TRY_CONVERT is available in all supported versions of SQL Server (including those in extended support) it is by far a much safer option.
The date range for DateTime is 1753-01-01 through 9999-12-31. The year is a valid datepart. It does not have to be a string if it is the year only. SELECT ISDATE('1753') will return 1 whereas SELECT ISDATE('1752') will return 0. See the full definition here.
Because any four digit number is a valid year, therefore it is a date, and the ISDATE() function checks an expression and returns 1 if it is a valid date, otherwise 0.
At 4000-2000 it only checks the return value (2000), which is still valid. If you would give it as a string by parameter ("4000-2000"), it would fail.

How to get rid of the following error? Converting date from string is failed

In SQL Server, I am getting the error:
Error -2147217913: Conversion failed when converting date an/or time from character string.
Error in converting
I am sure that my fields are in date field, but why this error keeps on showing?
First I validated first if the data type is really a date column:
CASE
WHEN ISDATE(dbo.AdditionalDetailInfo.UserDefined2) = 1
THEN dbo.AdditionalDetailInfo.UserDefined2
ELSE NULL
END AS ReceivedDate
I found out that yes, it's correct. so i proceed with converting this field where UserDefined2 value should always be plus 1 day.
CONVERT(VARCHAR(12), DATEADD(DAY, 1, CONVERT(DATETIME, dbo.AdditionalDetailInfo.UserDefined2)), 103) AS ReceivedDate
Please let me know if there's something wrong with my query as I really can't diagnose what went wrong.
See screenshot:
See this link, the actual data and in SQL server + 1 day
Maybe the field is indeed a date field, so it could be that no conversion is required. Try DATEADD(DAY, 1, dbo.AdditionalDetailInfo.UserDefined2).
If the column is indeed a string column, the you forgot to specify the style parameter when reading the value, you only used it when converting back. Try this instead:
CONVERT(VARCHAR(12), DATEADD(DAY, 1, CONVERT(DATETIME, dbo.AdditionalDetailInfo.UserDefined2, 103)), 103) AS ReceivedDate
MSDN says that date, time and datetime types are not allowed as arguments to ISDATE but in practical terms, passing a date to it:
SELECT ISDATE(getutcdate())
Causes the datetime to be converted to varchar implicitly, passed to ISDATE as varchar, which promptly then returns a 1
As such we've no guarantee that your column really is a date type like you say, so I'm guessing it's probably a string.
Really, what you should do is change your column to be a date type. Don't store dates as strings; if Microsoft had intended you to do this and thought for a second it was a good idea, they wouldn't have implemented other column types than char based ones
If you cannot convert your column to date then you'll need to find the bad value:
select * from table where isdate(column) =0
Note that your attempt to convert style 103 (dd/mm/yyyy) may be failing because you have data that isn't in this style eg Christmas as 12/25/2000
Isdate might not pick this up because your database date format is set to mm/dd/yyyy and isdate thinks it's ok. Change your database date format so that isdate can properly tell what is a good date and what is a bad date
https://learn.microsoft.com/en-us/sql/t-sql/functions/isdate-transact-sql?view=sql-server-2017
And then, please, for the love of doing this properly, use a date typed column, not a string typed one
We need more information. In particular, we need to know what data type the UserDefined2 is. If you tell us that, then we can give better replies.
Just a note that ISDATE is a pretty worthless function since it returns 1 if the string is convertible to datetime. But what if what you really have os datetime2? Or smalldatetime? Because of that, use TRY_CAST instead.
Also, you just gave us pieces of queries, expressions. We don't see the context. There is something called predicate pushing in SQL Server that can mess things up for you (SQL Server pushes a predicate deeper into the query resulting that an expression work over non-datetime values even though you think that you have excluded them in a WHERE clause.
Also, note that how a datetime value is interpreted when you have separators and use the old types (datetime or datetime2) depends on the login's language setting.
More info in http://karaszi.com/the-ultimate-guide-to-the-datetime-datatypes

DT_DBTIMESTAMP2 having only 3 digits

I'm having (DT_DBTIMESTAMP2,7)GETDATE() in SSIS Derived Column Transformation and Table column with datetime2(7).
Even though I set 7 Digit Second Scale in both, but seems it comes only 3 digit.
For example, I expected like '2018-05-02 16:45:15.6192346' but it comes '2018-05-02 16:45:15.6190000'.
The reason why I need the millseconds, I'd like to sort out the latest record from any duplications using timestamp. I realized only 3 digit second scale is not enough for this pourpose.
Except for Derived Column Transformation and Table Columns, is there any requrired setting in SSIS package? Any advices would be appreciated.
GETDATE() returns a datetime, you should use SYSDATETIME() instead. See documentation.
edit
As noted by Larnu, you are probably using SSIS expression GETDATE, rather that the sql expression GETDATE as I assumed. The point is more-or-less the same though. GETDATE returns a DT_DBTIMESTAMP, where "The fractional seconds have a maximum scale of 3 digits." (Source).
Although this is almost the same as what HoneyBadger has said, I'm expanding a little, as the OP isn't using the GETDATE() expression in SQL Server. The value 2018-05-02 16:45:15.619 could never be returned by GETDATE() (Transact-SQL) as it's only accurate to 1/300th of a second (thus the final digit can only every be 0,3, and 7 (technically 0, 333333333~ and 666666666~, which is why the final digit is a 7, as it's rounded up)).
In SSIS the GETDATE() expression returns a datatype of DB_TIMESTAMP. According to the Documentation:
A timestamp structure that consists of year, month, day, hour, minute,
second, and fractional seconds. The fractional seconds have a maximum
scale of 3 digits.
Thus, the last 4 characters are lost. Unfortunately, I don't believe there is a function in SSIS that returns the the current date and time to the accuracy you require. Thus, if you need this high level, you'll likely need to use an expression in SQL Server that does, such as SYSDATETIME() that HoneyBadger recommended.

SQL date values converted to integers

Ok, I can't understand this thing.
A customer of mine has a legacy Windows application (to produce invoices) which stores date values as integers.
The problem is that what is represented like '01.01.2002' (value type: date) is indeed stored in SQL Server 2000 as 731217 (column type: integer).
Is it an already known methodology to convert date values into integers (for example - I don't know - in order to make date difference calculations easier?)
By the way, I have to migrate those data into a new application, but for as much I googled about it I can't figure out the algorithm used to apply such conversion.
Can anybody bring some light?
It looks like the number of days since Jan 1st 0000 (although that year doesn't really exists).
Anyway, take a date as a reference like Jan 1st 2000 and look what integer you have for that date (something like 730121).
You then take the difference between the integer you have for a particular date and the one for your reference date and you that number of days to your reference date with the DATEADD function.
DATEADD(day, *difference (eg 731217 - 730121)*, *reference date in proper SQLServer format*)
You can adjust if you're off by a day a two.

Resources