How do I determine if text can be converted to Date? - sql-server

While converting and normalizing data from an Access database to a SQL Server database, I ran across an interesting issue: ISDATE() does not appear to successfully predict whether text can be cast to a date.
SELECT 'This will return an error' =
CASE
WHEN ISDATE('1/1-2010') = 1 THEN CAST('1/1-2010' AS date)
ELSE NULL
END
I thought this was a bit odd, as the ISDATE() MSDN article states that the function will return "1 if the expression is a valid date, time, or datetime value; otherwise, 0."
From what I could tell, this error is not mitigated by playing with either SET DATEFORMAT or SET LANGUAGE.
Nor does CONVERT() appear to have a different result than CAST().
Fortunately, ISDATE() does successfully predict whether text can be converted to a datetime.
SELECT 'This will return a datetime' =
CASE
WHEN ISDATE('1/1-2010') = 1 THEN CAST('1/1-2010' AS datetime)
ELSE NULL
END
So, I could use a workaround of casting to a datetime and then casting to a date.
SELECT 'This will return a date' =
CASE
WHEN ISDATE('1/1-2010') = 1 THEN CAST(CAST('1/1-2010' AS datetime) as date)
ELSE NULL
END
However, I wonder if I am missing something. Is there a cleaner way to cast text to a date? As a corollary, is the ISDATE() function not defined correctly on MSDN (and actually therefore a bit of a misnomer)?

Neither IsDate nor IsNumeric actually attempt to convert your value to the type in question. Instead, they tell you if the value in question looks like it could be converted to a date or number. SQL Server 2012 has addressed this with the TRY_PARSE function. In the interim, you need to do some cleanup on the data or create a function that does that cleanup.

Related

Snowflake - Date format conversion

In snowflake, I am unable to convert my column to date format. When I try casting, the date column is not correct.
Can anyone help me to convert the particular column in to a date field?
SELECT
CASE
WHEN TO_CHAR(date1,'DD/MM/YYYY') = TO_CHAR(date1,'DD/MM/YYYY')
THEN TO_CHAR(date1,'DD/MM/YYYY')
ELSE
TO_CHAR(date1,'MM/DD/YYYY')
END AS CALL_DATE
SELECT
Coalesce(
try_to_date(date1, 'DD/MM/YYYY'),
try_to_date(date1, ‘MM/DD/YYYY’)) as call_date
Under snowflake to_date errors if it fails to parse. Thus we should use the TRY_ version.
I do not know exactly when Oracle TO_DATE will return null, but if it does the first branch of the CASE will fall through to the ELSE branch as NULL = NULL is FALSE.
The original code probably should have been done with a COALESCE or NVL, as it more clear why you move alone the value chain. Unless Oracles COALESCE evaluates all values at the same time??

"The conversion of a datetime2 data type to a smalldatetime data type resulted in an out-of-range value."

I have a stored procedure causing a date conversion error. I don't know what changed as it's been working fine for many months but our development group is downstream of a national DW. There are two variables used to retrieve date range #DatePlus1 & #DayOffset.
#DatePlus1 is used in a dozen or so CTEs that I won't post unless asked for but I commented out the date calculation and hard set the date as '2017-10-31 00:00:00' and ran the procedure. It ran without error but I don't understand why.
The original code set the #DatePlus1 as Date type which generated same error in subject.
SELECT #DayOffset = ISNULL(#DayOffset, 0)
DECLARE #DatePlus1 DATETIME2(0) --Was date but changed to datetime2 while troubleshooting
SELECT
#DatePlus1 = CONVERT(DATE, DATEADD(dd, 1, DATEADD(dd, #DayOffset, GETDATE())))
A static value would NOT be interpreted any differently than the same value passed in a variable. Therefore the problem is not what you think it is.
You are testing with a static variable that is in-range for a smalldatetime datatype. And your test is successful.
Therefore the reason your test fails when you use a variable is because the value of the variable is out-of-range for a smalldatetime, OR results in some selection of a value that is out-of-range.
The problem is not that you are using a variable. The problem is that you are giving that variable an invalid value in some part of the code that you are not showing in your question.
To troubleshoot this, you need to find all the places in your code where you populate a smalldatetime variable or column, and use PRINT or SELECT statements to see what value you are trying to insert into the smalldatetime.
So far I've done 2 and it fails on 1 RequestDate comparison but not another but RequestDate appears in different tables.
So another thing to look for is places in your code where the value of your variable could get changed programmatically. I would look right between the two queries where the comparison works, and the one where it fails.

CAST int to DATE failure when used with INNER JOIN

I have the following query:
SELECT
T2.PseudoDateColumn
FROM
(
SELECT
*
FROM T1
WHERE
PseudoDateColumn <> -1
) T2
INNER JOIN T3
ON T2.T2_key = T3.T3_key
WHERE
CAST(CAST(T2.PseudoDateColumn AS VARCHAR) AS DATE)
BETWEEN
DATEADD(mm, -1, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0))
AND DATEADD(ms, -3, DATEADD(mm, 0, DATEADD(mm, DATEDIFF(mm, 0, GETDATE()), 0)))
PseudoDateColumn is an int in T1. Values that aren't real dates have a -1 placeholder. All other dates are real dates (just stored as an int). Because of this, I need to first filter out all of the -1 values before I cast PseudoDateColumn to a DATE, such that I can use BETWEEN. When I run this script without the join, it works fine. But, when I run it with the join, SQL throws an error:
"conversion failed when converting date and/or time from character
string"
I don't know why this is happening. It's like SQL is doing the CASTing even before the sub-query.
Update:
I mistakenly wrote SQL Server 2008. I'm actually running this on PDW AU5, which has SQL Server 2012. I don't think that makes a difference, though.
Also, here is some sample data in PseudoDateColumn. All values are in the form YYYYMMDD.
PseudoDateColumn
-----------------
20150112
20160305
20111009
I know this is an old post but I think this can help someone. I had a similar error when trying to convert/cast a VARCHAR input parameter to a UNIQUEIDENTIFIER in my inner join. All my data was valid and the conversion worked just fine if done outside the join, but it would fail during the join. In fact I was even getting results in the results pane of SQL Server Management Studio but the error was still being thrown. What worked for me was to use TRY_CAST or TRY_CONVERT instead of CAST or CONVERT. My friendly neighborhood DBA suggested this solution because it ensures early execution or something like that.
The most likely explanation for the behavior is a "bad" value stored in the PseudoDateColumn. But you should be able to debug that.
Storing "date" values in an integer column seems like an anti-pattern to me, when SQL Server has a perfectly suitable DATE datatype custom designed for storing date values.
But if you need to work with what you already have, and address the issue without altering the schema definition...
SQL Server has an ISDATE function that you could use test test the validity of a character string. Unfortunately, the check that ISDATE performs isn't as strict as an actual CAST operation, but it does catch the most egregiously "bad" values.
My recommendation:
Instead of converting the integers to dates (the integer values stored in your column...
Cast/convert your literal DATE values to integer, into the same datatype/format as the column (integer), and do the comparison to the raw integer column.
So your query would be something like this:
WHERE T2.PseudoDateColumn >= CONVERT(INT, CONVERT(VARCHAR(8), datexpr1, 112) )
AND T2.PseudoDateColumn < CONVERT(INT, CONVERT(VARCHAR(8), datexpr2, 112) )
In that example, dateexpr1 and dateexpr2 would be replaced with expressions that return a DATE datatype, like the expressions in the original query.
NOTE: This type of range comparison depends on the stored integer values to be canonical. (With year first (four digits), then month (two digits), then day (two digits). In that representation, we are guaranteed that the comparison
(int)a <= (int)b < (int)c
will yield the same results as
(date)a <= (date)b < (date)c
(Equivalent apart from the differences due to "bad" integer values that can't be converted to a valid date... for example, an integer value with invalid "month" and/or "day" parts, e.g. 20161300 (month is thirteen, and day is zero)
An integer comparison that checks for pseudodates the 2016 would "work" for that without throwing an error...
20160101 <= 20161300 < 2017000 -> TRUE
This approach entirely avoids the issue of attempting to convert a "bad" psuedodate integer value into a DATE. But it's always possible to convert a valid DATE into an integer in YYYYMMDD format.
As an added benefit of this pattern, with predicates on the bare PseudoDateColumn column, SQL Server may be able to make effective use of an index that has PseudoDateColumn as the leading column.
If you are dead set on converting PseudoDateColumn to a DATE, you could first perform a conditional test that validates the value, using the ISDATE function. Something like this:
CASE WHEN ISDATE(CONVERT(VARCHAR(20), T2.PsedoDateColumn)) = 1
THEN CONVERT(DATE,CONVERT(VARCHAR(20), T2.PseudoDateColumn),112)
ELSE NULL
END

Convert Time zonoffset value to varchar

Query:
DECLARE #TimeZoneOffset datetimeoffset
SELECT #TimeZoneOffset = Time_Zone_Offset
FROM OFFSET_TABLE WHERE Active=1
Time_Zone_Offset column contains value like -6:00 (only offset)
When I do SELECT #TimeZoneOffset it throws me an error
Conversion failed when converting date and/or time from character string.
I know I am doing something wrong. I may need to CONVERT/CAST but can't get o/p so far.
Any help
To visualize what is happening here, try this:
DECLARE #x VARCHAR;
SET #x = 'abcdefghijklmnop';
SELECT #x;
Result:
----------
a
You have silently lost data from your variable, because you didn't bother declaring a length for your VARCHAR. In your case, I think you are ending up trying to use the string - somewhere, as opposed to the string -6:00.
I'm not sure how a simple SELECT yielded the error you mentioned; I suspect you are using it in some other context you haven't shown. But please try it again once your variable has been declared correctly.
Now I see why, your question wasn't correct - you said you were converting to VARCHAR but you weren't. This is not really unexpected, as -6:00 is not a valid DATETIMEOFFSET value; there is expected to be date and time components as well, otherwise the data type would just be called OFFSET. A valid DATETIMEOFFSET, according to the documentation, is:
DECLARE #d DATETIMEOFFSET = '1998-09-20 7:45:50.71345 -05:00';
So perhaps you have some datetime value and you want to apply the offset, well you can use SWITCHOFFSET() for that. However -6:00 is not a valid value; it needs to be in [+/-]hh:mm format (notice the leading 0 above, which seems to be missing from your sample data). So this would be valid:
DECLARE #datetime DATETIME = GETDATE(), #offset VARCHAR(6) = '-06:00';
SELECT SWITCHOFFSET(#datetime, #offset);
You need to correct the data in your offsets table and you need to change the way you are using the output. Personally, I've found it easier to stay away from DATETIMEOFFSET and SWITCHOFFSET(), especially since they are not DST-aware. I've had much better luck using a calendar table for offsets, storing the offsets in minutes, and using DATEADD to switch between time zones. YMMV.

Is March 27th, 2012 of significance to SQL Server in a Varchar to Datetime conversion?

I have a stored procedure that takes a datetime parameter which is passed in as a string. Such as this:
Procedure:
CREATE PROCEDURE [dbo].[MyFancySP]
#MyStartDate datetime = NULL,
#MyEndDate datetime = NULL
AS
....
Call:
EXEC [dbo].[MyFancySP]
#MyStartDate = N'01/01/2012',
#MyEndDate = N'03/01/2012'
The stored procedure has been working like this forever. Now, here's the interesting part. As soon as I change the date to 03/27/2012 or past it, I get the following error: The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
The only place in the stored procedure where the dates are used is in the where clause. In case it has to do with it, I'll copy it in here as well:
WHERE
((#MyStartDate IS NOT NULL AND #MyEndDate IS NOT NULL
AND d.SomeDate >= #MyStartDate AND d.SomeDate <= #MyEndDate)
OR #MyStartDate IS NULL AND #MyEndDate IS NULL)
Any ideas why I'm getting the out of range exception on March 27th or beyond? This is running on SQL Server 2008 R2 by the way.
Thanks!
Execute the following on each new database connection.
SET DATEFORMAT DMY
After you do this, your problem should disappear. I suspect that your issue is conditioned by a combination of server locale, and whether the day-of-month is 13th to 31st or not.
Not only that you see the error, you may also be fetching data for incorrect periods without noticing; other layers of your software may be correcting for that, but maybe only in some cases.
What type is d.SomeDate? Is it a NVARCHAR by any chance? That would explain it, as the WHERE clause would contain in such case an implicit conversion that the rules of Data Type Precedence state that should occur as a DATETIME. The apparent randomness of the error occurs due to the simple fact that the query scans rows that have invalid dates in the d.SomeDate field. In such a case you're dealing with data purity issues and you should fix your tables, preferably by making the column a DATETIME.
In addition:
always use the canonical date format YYYYMMMDD (no delimiters) in string representation: EXEC [dbo].[MyFancySP]
N'20120101', N'20120301';. This format is independent of the host locale, DATEFORMAT and LANGUAGE settings, .
Read Dynamic Search Conditions in T-SQL. WHERE column=#value or #value is null stops query performance optimizations dead on its track. Read the article.

Resources