Snowflake - Date format conversion - snowflake-cloud-data-platform

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??

Related

Char conversion to date fails depending on where clause

I am trying to set up a simple query on my data converting char YYYYMMDD to date type. I am using simple convert(date, MyDateColumn).
Select isdate(MyDateColumn),
convert(date,MyDateColumn)
FROM MyTable
WHERE MyTimeColumn = '000000'
Result: Conversion failed when converting date and/or time from character string.
Select isdate(MyDateColumn),
MyDateColumn
FROM MyTable
WHERE MyTimeColumn = '000000'
Result: 1 20190821
Select isdate(MyDateColumn),
convert(date,MyDateColumn)
FROM MyTable
WHERE MyDateColumn = '20190821' AND MyTimeColumn = '000000'
Result: 1 2019-08-21
I have observed that this query has failed for me for specific row above depending whether I added where clause to MyDateColumn. I have added where clause MyDateColumn=MyDateColumn as a workaround. Any idea for a better solution?
I am using SQL Server 2016 (13.0.5201.2). MyDateColumn is char(10) type
The best solution here is to stop storing your dates as text, in text columns, and to start using actual date columns instead. The conversion failure in your very first query is alarming, and would seem to indicate that either you have non dates stored, or maybe there is some trailing/leading whitespace. Since you are using SQL Server 2016, you may try the following query to flush out any non conforming date strings:
SELECT MyDateColumn
FROM MyTable
WHERE TRY_CONVERT(datetime, MyDateColumn) IS NULL;
Once you have located the problematical records, you may fix them, and then give your queries another try.

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

Comparing dates stored as varchar

I need to compare dates that are stored in my database as varchar against today's date.
Specifically, I need to exclude any records with a date that has passed.
I tried:
SELECT * FROM tblServiceUsersSchedule
WHERE ScheduleEndDate !='' AND ScheduleEndDate < '2015/05/31'
This selected values such as 17/06/2012 and 19/04/2015, which have both already passed, along with 01/06/2015 which hasn't.
I then tried to cast the data with:
SELECT *
FROM tblServiceUsersSchedule
WHERE CAST(ScheduleEndDate as DATETIME) < CAST('05/31/2015' as DATETIME) AND ScheduleEndDate !='' AND ScheduleEndDate is not null
But got the following error:
The coversion of a varchar data type to a datetime data type resulted
in an out-of-range value.
I checked the data behind and none are null, none are blank white space. All are dates in the format of dd/mm/yyyy.
I can't figure out how to compare the varchar date stored with todays date.
Storing date values as varchar is simply wrong.
If possible, you should alter the table to store them as date data type.
You can do it in a few simple steps:
Rename the current columns (I'm guessing ScheduleStartDate is also varchar) to columnName_old. This can be easily done by using sp_rename.
Use alter table to add the columns with the appropriate data type.
Copy the values from the old columns to the new columns using an update statement. Since all of the dates are stored in the same format, you can use convert like this: set ScheduleStartDate = convert(date, NULLIF(ltrim(rtrim(ScheduleStartDate_old)), ''), 103) If your sql server version is 2012 or higher, use try_convert. Note i've used the nullif, ltrim and rtrim to convert values that only contains white spaces to null.
Drop and recreate indexes that is referencing these columns. The simplest way to do this is by right-clicking the index on SSMS and choose script index as -> drop and create.
Use alter table to remove the old columns.
Note: if these columns are being referenced in any other objects on the database you will have to change these objects as well. This includes stored procedures, foreign keys etc`.
If you can't change the data types of the columns, and your sql server version is lower then 2012, you need to use convert like this:
SELECT * FROM tblServiceUsersSchedule
WHERE CONVERT(DATE, NULLIF(ScheduleEndDate, RTRIM(LTRIM('')), 103)
< CAST(GETDATE() As Date);
AND ScheduleEndDate IS NOT NULL
Note that if you have even a single row where the column's data is not in dd/MM/yyyy format this will raise an error.
For sql server versions 2012 or higher, use Try_convert. This function will simply return null if the conversion fails:
SELECT * FROM tblServiceUsersSchedule
WHERE TRY_CONVERT(DATE, NULLIF(ScheduleEndDate, RTRIM(LTRIM('')), 103)
< CAST(GETDATE() As Date);
AND ScheduleEndDate IS NOT NULL
Note: I've used CAST(GETDATE() as Date) to remove the time part of the current date. This means that you will only get records where the ScheduleEndDate is at least one day old. If you want to also get the records where the ScheduleEndDate is today, use <= instead of <.
One final thing: Using functions on columns in the where clause will prevent Sql Server to use any indexing on these columns.
This is yet another reason why you should change your columns to the appropriate data type.
Other than what people have already suggested that you should never store DATETIME as VARCHAR. Always store it in a DATETIME type column; I think you should change your condition in WHERE
ScheduleEndDate < '2015/05/31'
To this, in order to get all dates which hasn't passed yet
ScheduleEndDate >= '2015/05/31'
Your query should look like
SELECT * FROM tblServiceUsersSchedule
WHERE ScheduleEndDate IS NOT NULL
AND ScheduleEndDate >= '2015/05/31'
If you HAVE TO store your dates as varchar (and as per other other answers, this is a poor practice), then using an ISO style format like yyyy-mm-dd should allow textual comparisons without issue.
If your column is a date data type, and you're using SQL 2012 or later then use DATEFROMPARTS (or one of its variants) for date comparison, so
WHERE DateToCompare < DATEFROMPARTS (2019, 12, 31)
rather than
WHERE DateToCompare < '2019-12-31'
SQL handles the latter fine, but the former is more "correct".

Converting Date Strings (in different formats) Into Data Type In SQL Server

I have a column of data in the below string formats
When I copy and paste the above into an Excel sheet and change the column to a date format, all of the above will be converted into the same format correctly. Is there a similar feature in SQL server? I've tried using CAST and CONVERT, but have ran into conversion failed errors.
Follow-up
I found the values causing standard CAST and CONVERT functions to fail. There's some rows with decimal and int values as follows:
I ended up writing a CASE statement to account for the different format variations:
CASE
WHEN [CommissionStart Date] LIKE '%NULL%'
THEN '1/1/1900'
WHEN ISDATE([CommissionStart Date]) = 1
THEN CAST([CommissionStart Date] AS DATE)
ELSE CONVERT(DATETIME, CAST([CommissionStart Date] AS DECIMAL(11, 5)))
END
Cast or Convert should work provided the dates are all in a valid format. If a value can't be parsed as a date, there's no magic fix. You have to find them and deal with them manually. The IsDate() function can help you find them.
select * from MyTable where IsDate(TextDateColumn) = 0
IsDate() will return 0 for numeric values like 4567 or 5456.6826 so if you have a lot of those then you can also check IsNumeric()
select * from MyTable where IsDate(TextDateColumn) = 0 and IsNumeric(TextDateColumn) = 0
That should allow you to identify the unparseable dates that are causing the conversion errors.

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

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.

Resources