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.
Related
I've been having problems with a query that returns data between two date times, the query that I'm trying to fix is this one
pay.date BETWEEN '01/06/2020 00:28:46 a. m.' AND '01/06/2020 10:38:45 a. m.'
That query does not detect the a. m. part and if I have a payment at 10 am and 10 pm it will detect both payments as the t. t. part is not detected, I've been searching for a while now with no luck, thanks in advance :)
Do the filtering by an actual datetime type:
cast(replace(replace(pay.date, ' a. m.', 'am'), ' p. m.', 'pm') as datetime)
It might be better to use convert() so you can specify the proper format. If you can't supply the date literals in a readily convertible format then do a similar replace and cast on those too.
Use a literal format that is unambiguous and not dependent on runtime or connection settings. More info in Tibor's discussion.
In this case:
where pay.date between '20200601 00:28:46' and '20200601 10:38:45'
Notice that I assume June, not January - adjust as needed. Between is inclusive and be certain that you understand the limitations of the datatype for pay.date. If datetime, the values are accurate to 3ms. Verify that your data is consistent with your assumption about accuracy to seconds.
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.
Consider the following demonstration queries and the results (note the only difference in the two queries is the comparison operator in the WHERE clause):
The LUpd_DateTime column is a smalldatetime data type. Since the smalldatetime data type doesn't actually contain any seconds (rounding occurs up or down to the nearest minute), the only explanation I have for the two queries below is that SQL Server is converting the date string to a smalldatetime type and rounding up to the nearest minute, thus changing the date string to '9/20/2018 00:00:00 AM'.
Can anyone confirm this?
SQL Server is converting the date string to a smalldatetime type and
rounding up to the nearest minute, thus changing the date string to
'9/20/2018 00:00:00 AM'. Can anyone confirm this?
Yes. To compare two expressions SQL Server always converts both expressions to a common data type. Whichever expression's data type has the lower Data Type Precedence is converted. The "date string" is an expression of type varchar which has a lower precedence than smalldatetime. So the string is converted to smalldatetime for comparison. And you can verify that the conversion rounds to the nearest value:
select cast( '2018-09-19 11:59:59' as smalldatetime)
outputs
2018-09-19 12:00:00
I think you may have mistyped your explanation? You state that the column is a smalldatetime but then go on to say you think the query is converting the "date string to a smalldatetime". If what I said is correct, then a simple check of logic will show your assumption to be true. Yes, when converting it will become "09/20/2018 00:00:00 AM".
DECLARE #dateField AS date
SET #dateField = '2018-09-20 06:23:00'
SELECT CONVERT(smalldatetime, #dateField)
It is covered clearly in the documentation.
Defines a date that is combined with a time of day. The time is based
on a 24-hour day, with seconds always zero (:00) and without
fractional seconds.
If you look at your results all are 00 seconds.
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
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.