IsDate Function in SQL evaluates invalid dates as valid - sql

I am running a SQL Statement against imported data from Excel Files.
In this SQL I am checking if the users have entered dates properly by using IsDate function. Since this is a raw data that hasn't been converted yet, all dates are stored in a varchar data type field.
In some circumstances IsDate returns 1 (valid date) when there is clearly an incorrect date format entered by the user.
For Example:
07/001/2012
2012-07-002
007/002/2012
Any Suggestions on how to handle this problem?
SELECT *
FROM tblImport
WHERE (ISDATE(dt) = 0
AND (dt is not null AND dt <> ''))
Thanks!
p.s. Smacking users' did not help.

I do a lot of data conversion work and here is a function that I created and use it practically everyday to weed out the bad dates:
CREATE FUNCTION dbo.fnCheckDate
(#InDate nvarchar(50))
RETURNS DATETIME
AS
BEGIN
declare #Return DATETIME
select #return = CASE WHEN ISDATE(#InDate) = 1
THEN CASE WHEN CAST(#InDate as DATETIME) BETWEEN '1/1/1901 12:00:00 AM' AND '6/6/2079 12:00:00 AM'
THEN #InDate
ELSE null
END
ELSE null
END
return #return
END
GO
Results:
SELECT dbo.fnCheckDate('07/001/2012') --> Returns 2012-07-01 00:00:00.000
SELECT dbo.fnCheckDate('2012-07-002') --> Returns 2012-07-01 00:00:00.000
SELECT dbo.fnCheckDate('007/002/2012') --> Returns 2012-07-01 00:00:00.000
SELECT dbo.fnCheckDate('00/002/2012') --> Returns Null
SELECT dbo.fnCheckDate('006/031/2012') --> Returns Null
SELECT dbo.fnCheckDate('') --> Returns Null

Try setting the dateformat first - that worked for me when I was seeing exceptions.
set dateformat dmy
select IsDate(<column>)
from Table

maybe just check dt's LEN? however, it can not handle cases when the len is valid. maybe input validation should happen in the frontend?

Related

Handling a value of zero in a date datatype

I'm reporting out of a database that is using decimal(17,6) as the datatype for a date field. For example, the current date/time in this field would be 20210820.171900. Unusual, but whatever. I need to convert the original date field from decimal(17,6) to datetime. This is what I have:
SELECT convert(datetime, convert(varchar,convert(int, lastmoddatetime)), 0)
from Table1
The above statement works correctly as long as none of the records have a value of zero in this column. Unfortunately, the column value defaults to zero (0.000000) if no date has been calculated for it. Whenever a column has a zero value, I get the following error:
Conversion failed when converting date from character string.
How can I overcome this issue? Ultimately, I'm needing to apply a dateadd function to the lastmoddatetime field.
Note: Before you suggest changing the column definition, this database originated in the 1990's and I'm not allowed to make any changes to the database structure.
You can use NULLIF to null out those values
convert(datetime, convert(varchar(15), convert(int, NULLIF(lastmoddatetime, 0.0))), 0)
Either use TRY_CONVERT or CASE - depending how you want to handle the zero case.
SELECT
-- If desiring null for 0 and SQL Server 2012+
TRY_CONVERT(date, CONVERT(varchar, CONVERT(int, lastmoddatetime)), 0)
, CASE WHEN lastmoddatetime <> 0
-- If desiring some other valid date or < SQL Server 2012
THEN CONVERT(date, CONVERT(varchar, CONVERT(int, lastmoddatetime)), 0)
ELSE NULL /* Whatever valid datetime value you want */ END
FROM (
VALUES (20210820.171900), (0.0)
) x (lastmoddatetime);
I note that this ignores the time component - so am converting to a date not datetime above. If you need to handle the time component you need to update your question.
Yet another option.
You can thin it out a bit by using left() and try_convert()
Example
Declare #YourTable table (lastmoddatetime numeric(17,6))
Insert into #YourTable values
(20210820.171900)
,(0.0)
Select AsDate = try_convert(date,left(lastmoddatetime,8))
,AsDateTime = try_convert(datetime,left(lastmoddatetime,8))
From #YourTable
Results
AsDate AsDateTime
2021-08-20 2021-08-20 00:00:00.000
NULL NULL
use
convert(datetime,convert(int,lastmoddatetime),0)

Create Sybase function with exception handling

I want to create function to use it in creation of view.
In my table there are strings (strings are consists only of 8 digits) that I'm converting into DATE.
My function is:
CREATE FUNCTION MY.FUNCTION(#date int)
RETURNS DATE
AS
BEGIN
RETURN CONVERT(DATETIME, #date)
END
If I use smth like SELECT FUNCTION('20170323') FROM TABLE it works as expected.
But if I'll try smth like SELECT FUNCTION('77777777') FROM TABLE it fails of course... But if it fail I need to retut NULL!
After some digging I have no result about function modification.
How to add exception handling in my function properly to return date on NULL if it fails?
use TRY_CONVERT instead of CONVERT, which would be :
CREATE FUNCTION TEST(#date varchar(50))
RETURNS DATETIME
AS
BEGIN
RETURN TRY_CONVERT(DATETIME, #date)
END
Result:
select [dbo].[TEST]('20171201') --output:2017-12-01 00:00:00.000
select [dbo].[TEST]('9999999999') --output: NULL
After a long investigation and lot of efforts I've found my solution:
CREATE FUNCTION MY_FUNCTION(#date CHAR(20))
RETURNS DATE
AS
BEGIN
RETURN
(CASE
WHEN ISDATE(#date) = 0
THEN NULL
ELSE CAST(#date AS DATE)
END)
END
Sybase method ISDATE() doing all magic in this case without throwing exception...

SQL Server ISDATE before conversion returns error "Conversion failed when converting date and/or time from character string"

I have been given a task of converting dates on a field that is define as Varchar(50). As expected the dates inside the column were a bit out of the normal formats like this DECEMBER 11, 2011
I have used ISDATE(datefield) but it says
Conversion failed when converting date and/or time from character string
My second option was to update the column using this statement
UPDATE dbo.tbl_arrest_information
SET date_arrested = CASE
WHEN ISDATE(date_arrested) > 0
THEN CONVERT(datetime, date_arrested)
ELSE '01/01/1901'
END
I use '01/01/1901' because it doesn't accept null values but it returns an error stating that
String or binary data would be truncated
I am well aware that the date field has a length of 50 so i am at lost why it is happening.
Any advise would be helpful.
Thank you
I'm going to guess that your date_arrested might be a smalldatetime field which will throw errors that ISDATE() will not find because it assumes you are checking for a datetime field. You may have to do something like this:
UPDATE dbo.tbl_arrest_information
SET date_arrested =
CASE WHEN ISDATE(date_arrested) > 0
THEN CASE WHEN date_arrested BETWEEN '1900-01-01' AND '2079-06-05' THEN CONVERT(datetime, date_arrested) ELSE '01/01/1901' END
ELSE '01/01/1901' END
You Should Use Correct LANGUAGE And DATEFORMAT With ISDATE()
SET LANGUAGE us_english;
SET DATEFORMAT mdy;
SELECT ISDATE('04/15/2008'); --Returns 1.
Returns 1
SET LANGUAGE us_english;
SET DATEFORMAT myd;
SELECT ISDATE('15/04/2008'); --Returns 0.
Also USE CONVERT ( 1, 2 ,3 )

Convert INT data type into DateTime Format

//This is my script
SELECT CASE WHEN iPaid_dt>= 1 THEN CONVERT(DATETIME, CONVERT(CHAR(8),iPaid_dt)) ELSE '' END AS [TRANSACTION DATE]
FROM MyTable
//My OutPut is
1900-01-01 00:00:00.000
2012-11-11 00:00:00.000
2012-10-26 00:00:00.000
//Expected Output:
2012-11-11 00:00:00.000
2012-10-26 00:00:00.000
//The 1900-01-01 output should be '' only, What is the correct script? note: iPaid_dt is INT with 0 as default value.
A CASE expression must have a single data type that all possible values for all rows must belong to. Since DATETIME has a higher precedence than VARCHAR, all of the possible values are being forced to be DATETIMEs. So the '' is converted back to a DATETIME.
I can't say how to fix it since I don't know why you're doing this conversion in the first place. Maybe NULL would be a better placeholder than ''?
You're mixing field types and SQL Server is converting '' to a DATETIME for you. Try this:
SELECT CASE
WHEN iPaid_dt > 0
THEN CONVERT(NVARCHAR(23),
CONVERT(DATETIME, CONVERT(CHAR(8),iPaid_dt)))
ELSE ''
END AS [TRANSACTION DATE]
FROM MyTable

SQL 2008 CASE statement aggravation

Why does this fail:
DECLARE #DATE VARCHAR(50) = 'dasf'
SELECT CASE WHEN ISDATE(#DATE) = 1 THEN CONVERT(date,#DATE) ELSE #DATE END
Msg 241, Level 16, State 1, Line 2
Conversion failed when converting date
and/or time from character string.
Why is it trying to convert dasf to date when it clearly causes ISDATE(#DATE) = 1 to evaluate to false...
If I do:
SELECT ISDATE(#DATE)
The return value is 0.
CASE returns a single type. In this case, the type is Date, found from your THEN clause. It is implicitly converting the ELSE clause result to Date to match.
You must choose a single type to be returned by CASE. It cannot be used to return sometimes Date and sometimes varchar.
from MSDN:
http://msdn.microsoft.com/en-us/library/ms181765.aspx
Return Types
Returns the highest
precedence type from the set of types
in result_expressions and the optional
else_result_expression. For more
information, see Data Type Precedence
(Transact-SQL).
and then following that link: http://msdn.microsoft.com/en-us/library/ms190309.aspx
8) date
27) varchar
It's not clear what you want, so it's hard to offer alternatives (I don't know if the CASE is part of a larger query or script), but here's a couple things you can do:
-- choose a single return type per CASE expression
SELECT
CASE
WHEN IsDate(#Date) = 1
THEN convert(date, #Date)
ELSE null
END as [Date],
CASE
WHEN IsDate(#Date) = 1
THEN null
ELSE #Date
END as [VarChar]
--use control flow to select what you want.
IF IsDate(#Date) = 1
THEN
SELECT convert(date, #Date)
ELSE
SELECT #Date
try this:
DECLARE #DATE VARCHAR(50) = 'dasf'
SELECT CASE
WHEN ISDATE(#DATE)=1 THEN CONVERT(char(23),CONVERT(date,#DATE),121)
ELSE #DATE
END
It will basically format your valid date and leave the non-dates alone. Is that what you are after?
actual working sample:
DECLARE #YourTable table (DATE VARCHAR(50))
INSERT #YourTable VALUES ('dasf')
INSERT #YourTable VALUES ('1/1/2010')
SELECT
CASE
WHEN ISDATE(DATE)=1 THEN CONVERT(char(23),CONVERT(datetime,DATE),121)
ELSE DATE
END AS DATE
FROM #YourTable
OUTPUT:
DATE
--------------------------------------------------
dasf
2010-01-01 00:00:00.000
(2 row(s) affected)
In the working example, I made a substitute from date data type to datetime because I'm on SQL Server 2005 and date datatype is SQL Server 2008 only.