Is there a safe way to cast SQL datetimes? - sql

Pretty simple question that I can't quite find the answer for:
Is there a simple and safe† way to cast a varchar to datetime in SQL Server?
† i.e. gracefully handle non-datetime strings with a default datetime value

You can filter your rows using the isdate function. This query returns three rows without any conversion errors:
with v as (
select '20110714' value union all
select '2011-07-15' union all
select '3/22/2011' union all
select 'foo'
)
select cast(value as datetime)
from v
where isdate(value) = 1
Edit
When you want a default vaue (like the current date/time), you could do something like this:
select case when isdate(value) = 1 then cast(value as datetime) else getdate() end

From SQL Server Denali you can use TRY_CONVERT. A case expression is the only safe way in previous versions.

You could try
select cast ('28/08/2006 11:23:25' as datetime)
SELECT * FROM tab_val WHERE (CASE ISDATE(val) WHEN 1 THEN CAST(val As DateTime) ELSE NULL END)
More details in the manual

we can place a check that column have the valid datetime and if not then return nulls not genrating any error.
SELECT CASE ISDATE([YourDate]) WHEN 0
THEN CAST([YourDate] AS DATETIME)
ELSE CAST(NULL AS DATETIME) END AS [Your Date]
FROM [dbo].[yourtable]

Related

Cast to datetime only if its a date value

I have a table with DateFrom column
it's an nvarchar col in the col I have data like that
01
02
10/10/2020
04
some strings and some DateTime values
I need to cast it to DateTime but only if it's a date if not then pull out the value like it is can is this possible?
thanks ...
a Simple Try cast should do the job here
DECLARE #Table TABLE (Val NVARCHAR(20))
INSERT INTO #Table
VALUES('1'),('2'),('10/10/2020'),('04')
SELECT
*,
TRY_CAST(Val AS DATE)
FROM #Table
Results
Something like this should work. It uses a CASE statement to check whether or not the value is a valid date and if so casts it to DATETIME, then converts it to VARCHAR (so dates and other values can be returned as the same column).
You can find other datetime to string styles here if you need them formatted differently:
https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver15
DECLARE #TempTable TABLE (Dt NVARCHAR(30))
INSERT INTO #TempTable
VALUES('1'),('2'),('10/10/2020'),('04'),('Jun 27 2021 12:22AM'),('Oct 12 2021 8:31PM')
SELECT
*,
CASE WHEN ISDATE(Dt) = 1 THEN Convert(VARCHAR, CAST(Dt AS DATETIME), 120) ELSE Dt END
FROM #TempTable
You can easily check this with the case statement.
Postgres example
select case when '12/12/2021' ~ '\d{2}\/\d{2}\/\d{4}' then 'this is the date' ELSE 'this is a string' end;
->this is the date
select case when 'some text' ~ '\d{2}\/\d{2}\/\d{4}' then 'this is the date' ELSE 'this is a string' end;
->this is a string
so you can use it like this
select case when fild_01 ~ '\d{2}\/\d{2}\/\d{4}' then TO_DATE(fild_01,'DD/MM/YYYY')::text ELSE fild_01 end from test_table;
as you can see, case construction can't generalize two different types of data, so I had to convert them to the same type of text
Use the TRY_CONVERT function, with style 107
SELECT TRY_CONVERT(datetime, 'Jun 27 2021 12:22AM', 107)
Result
2021-06-27 00:22:00.000
This returns a null if the value is not a date
You can choose according to your needs.
When you want to set default value:
SELECT IIF(ISDATE(Val) = 1 , Val,'YOUR_DEFAULT_VALUE')
Whent default value not matter (It will be NULL):
SELECT TRY_CAST(NULL AS DATE)

SQL: Cast Not working in SubString

We have a string field for date of birth and now we have to convert it in order to perform the calculations required. However when we are using CAST or CONVERT to convert to perform the calculations it is not working.
select distinct(ptr.RecordID)
from dbo.PatientRecord as ptr
where
ptr.CHName like 'Access2Loc%'
AND ptr.RecordID
in(
select
(
case when
(DATEDIFF(hour, convert(date,DOB,110), GETDATE())/8766)>18
then PatientID
else NULL
end
) as RecordID
from
PatientView
where ISDATE(DOB) = 1
)
SQL Server considers this a "feature". It is hard to explain, but the where is not necessarily executed before the select.
In SQL Server 2012+, use try_convert() (or try_cast():
where ptr.CHName like 'Access2Loc%' and
ptr.RecordID in (select (case when DATEDIFF(hour, try_convert(date, DOB, 110), GETDATE()) / 8766 > 18
then PatientID
end) as RecordID
from PatientView
where ISDATE(DOB) = 1
)
In more ancient versions, you can use a case expression for much the same effect.
-- this will guarantee that ##t1 contains only valid dob rows
if object_id('tempdb..##t1') is not null
drop table #t1
select * into ##t1 from patientview where isdate(dob)=1
select distinct(ptr.RecordID)
from dbo.PatientRecord as ptr
where
ptr.CHName like 'Access2Loc%'
AND ptr.RecordID
in(
select
(
case when
(DATEDIFF(hour, convert(date,DOB,110), GETDATE())/8766)>18
then PatientID
else NULL
end
) as RecordID
from
##t1
)
drop table ##t1

"CASE" statement within "WHERE" clause in SQL Server 2012

i have a select statement like this
select x, y , z from table a
where CAST( CAST(DATEColumn AS VARCHAR) AS DATE) between
case when a.columnx = '1900-01-01' then CAST( CAST(DATEColumn AS VARCHAR) AS DATE) else c.columnx end
and cast( c.CustomerShipToRecTrmDt as date)
why does this case statement does not work
whereas the case statement works if i hardcode the dates?
What are you casting a date columns to varchar and then back to a date? I think this does what you want:
select x, y , z
rom table a
where DATEColumn between
(case when a.columnx = '1900-01-01' then DATEColumn else cast(c.columnx as date) end and
cast( c.CustomerShipToRecTrmDt as date)
You can eliminate the case statement from the where clause by doing:
where DATEColumn >= cast(c.columnx as date) and DateColumns <= cast( c.CustomerShipToRecTrmDt as date)
The comparison to 1900-01-01 doesn't affect the >= part, unless you are working with dates before 1900 (I'm guessing this is unlikely).
Also, whenever casting values to varchar(), always include the length (as in varchar(255)).

Split and select specific values from sqlserver table

I have a table projects in SQL server having a field StartDate with values as
15-02-2013 15:02:40
20-08-2011 10:11:20
etc
From which i need to retrieve distinct year values like
2013 and 2011 only
I tried with queries like
select (case when charindex(' ', StartDate) > 0
then left(StartDate, charindex(' ', (StartDate))-1)
else StartDate
end) as StartDate FROM [kneipp].[dbo].[kn_projects]
which gave result as 15-02-2013 and 20-08-2011
and with
select (case when charindex('-', StartDate) > 0
then right(StartDate, charindex('-', reverse(StartDate))-1)
else StartDate
end) as lastone FROM [kneipp].[dbo].[kn_projects]
got 2013 15:02:40 and 2011 10:11:20
How to achieve my expected result
did you try expression like this?
SELECT distinct year( convert(datetime,StartDate,105))
FROM [kneipp].[dbo].[kn_projects]
You can CAST the column into a DATETIME format
SELECT YEAR(CAST(StartDate AS DATETIME)) FROM [kneipp].[dbo].[kn_projects]
As others have pointed out you can convert to datetime ,if you want to retain the data type then try the below query
Declare #t varchar(50),#t1 varchar(50)
set #t='15-02-2013 15:02:40'
Select left(parsename(replace(#t,'-','.'),1),4)
If you have datetime in any format then try the below expression which will return the year in varchar data type
Select left(parsename(replace(#t1,'-','.'),1),
charindex(' ',parsename(replace(#t1,'-','.'),1)))

SQL: Error when trying to filter out all dates older than the current date

I'm using SQL SERVER 2008.
I have a table that stores dates in datetime format (i.e.2012-01-21 15:00:00.000)
I'm trying to filter out all the dates older than "today". So I was attempting to do so by using the query below.
SELECT Date
FROM MyTable
WHERE Date >= GETDATE()
When I run that though, I get the following error.
Conversion failed when converting date and/or time from character string.
Is there something I'm doing wrong? Thanks for the help and let me know if I need to provide more information!
More Information:
[Date] is of type DateTime in MyTable.
I also have a View that simply selects [Date] and does no manipulation
I'm accessing [Date] via the View
It looks like your column is not a DATETIME data type after all. It is probably VARCHAR or similar. If you provide the DDL for the creation of the table, that would allow a more specific answer.
You don't say what locale your in as this will matter. The most likely possibility is that your DATE column isn't a DATETIME. You don't say what locale your in, but (as an example) US and UK datetime formats are treated differently when in reverse format.
UK sees the date as yyyy-dd-mm hh:mm:ss.fff
US sees the date as yyyy-mm-dd hh:mm:ss.fff
For example, this throws an error:
SET LANGUAGE british
GO
SELECT CAST ('1999-01-21 10:11:12.345' AS DATETIME)
GO
However if you change the locale to us_english, it will parse correctly.
If you want to gurantee it's always going to be parsed as yyyy-mm-dd, then you need to be strict and use the full ISO spec by specifying Z between the date and time, e.g.,: 1999-01-21Z10:11:12.345 will parse in the same way in both locales.
Ultimately, you want to change the Date column to a datatype of DATETIME, but you may need to temporarialy change your locale to be able to do this sucessfully; i.e.:
SET LANGUAGE us_english
GO
ALTER TABLE [...]
GO
SET LANGUAGE british
GO
Yet another fun 'gotcha' to watch out for when manipulating date/time data.
Sidenote: no I don't know why the Microsoft think us over in Blighty see the date as yyyy-dd-mm ... I've never encountered this format. Could be inherited from European formats?
First of all you should change this to:
SELECT [Date]
FROM MyTable
WHERE [Date] <= GETDATE()
That type of Column is [Date]????
If your date column is VARCHAR/NVARCHAR you need to convert that value to DATETIME. From your example, the right format should be:
SELECT [Date]
FROM MyTable
WHERE CONVERT(DATETIME,[Date],121) >= GETDATE()
SQL will implicitly convert a varchar to a datatime if you are comparing it to a datetime. The issue you're getting is that there is a record in your table that is not in a datetime format.
GOOD EXAMPLE:
SELECT *
INTO #Temp
FROM
(
SELECT '2010-01-21 15:00:00.000' [Date] UNION
SELECT '2012-01-21 05:30:00.000' [Date] UNION
SELECT '2015-01-21 07:45:00.000' [Date] UNION
SELECT '2020-01-21 11:20:00.000' [Date]
) x
SELECT *
FROM #Temp
WHERE [Date] > GETDATE()
DROP TABLE #Temp
BROKEN EXAMPLE:
SELECT *
INTO #Temp
FROM
(
SELECT '2010-01-21 15:00:00.000' [Date] UNION
SELECT '2012-01-21 05:30:00.000' [Date] UNION
SELECT '2015-01-21 07:45:00.000' [Date] UNION
SELECT 'a2020-01-21 11:20:00.000' [Date]
) x
SELECT *
FROM #Temp
WHERE [Date] > GETDATE()
DROP TABLE #Temp
The 'a' in the last record of the broken example will cause the error your getting.
WORK AROUND
SELECT *
INTO #Temp
FROM
(
SELECT '2010-01-21 15:00:00.000' [Date] UNION
SELECT '2012-01-21 05:30:00.000' [Date] UNION
SELECT '2015-01-21 07:45:00.000' [Date] UNION
SELECT 'a2020-01-21 11:20:00.000' [Date]
) x
SELECT *
FROM
(
SELECT CASE WHEN ISDATE([Date]) = 1 THEN [Date] ELSE '' END [Date]
FROM #Temp
) x
WHERE [Date] > GETDATE()
DROP TABLE #Temp
BETTER OPTION
SELECT *
INTO #Temp
FROM
(
SELECT '2010-01-21 15:00:00.000' [Date] UNION
SELECT '2012-01-21 05:30:00.000' [Date] UNION
SELECT '2015-01-21 07:45:00.000' [Date] UNION
SELECT 'a2020-01-21 11:20:00.000' [Date]
) x
SELECT *
FROM #Temp
WHERE CASE WHEN ISDATE([Date]) = 1 THEN [Date] ELSE '' END > GETDATE()
DROP TABLE #Temp