Char conversion to date fails depending on where clause - sql-server

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.

Related

Build a SQL query with a DateTime parameter

I can successfully use a Query with a Date parameter in string format as
SELECT * FROM ORDERS WHERE [DATE]='20160209'
but I haven't seen any sample of a Query specifying a DateTime parameter in string format.
Next samples are rejected by Microsoft SQL Server Management Studio:
SELECT * FROM ORDERS WHERE [DATE]='20130523T153500000Z'
SELECT * FROM ORDERS WHERE [DATE]='2013-05-23T15:35:00:000Z'
I know this is not a good practice and I should pass DateTime values rather than strings, but sometimes it is useful for debugging.
What is the right format to include a string formatted datetime on a SQL query?
No so sure where you've got those date formats...
This one '2013-05-23T15:35:00:000Z' just doesn't seem to be right. I haven't seen that nanoseconds were delimited by a ':' character. It is usually a decimal of a second, so '2013-05-23T15:35:00.000Z' is a better format and it works:
select convert(DateTime,'2013-05-23T15:35:00.000Z')
As for the other, you might need to do the parsing yourself:
select CONVERT(DATETIME,LEFT('20130523T153500000Z',4)+SUBSTRING('20130523T153500000Z',5,2)+SUBSTRING('20130523T153500000Z',7,2))
hope this helps.
Can you just do something like this?
SELECT *
FROM ORDERS
WHERE [DATE] = CONVERT(DATETIME,'20130523T153500000Z')
As long as the string is in a workable format.
If it's just for debugging, you might do something like:
DECLARE #val VARCHAR(25)
-- Easily swapped out with different testing values
SET #val = '20130523T153500000Z'
SELECT *
FROM Orders
WHERE [DATE] = CAST(#val AS DATETIME)
-- You could also use CONVERT

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".

Convert varchar to date with malformed values in SQL Server 2008

This whole date code is beyond my understanding but non the less I need to convert a column in a query result from varchar to date. I have some malformed values and I'm trying to filter them out.
SET DATEFORMAT dmy
SET LANGUAGE us_english
select convert(datetime, some_date, 103)
from some_table
where isdate(some_date) = 1
I keep getting
Conversion failed when converting date and/or time from character string
When querying for the malformed values I get these two records
some_date
---------
621
232
Is there any way to validate the format using isdate ?
Thanks, Frustrated Oracle DBA.
EDIT
Tried #Sean Lange suggestions and indeed it looked promising
select convert(datetime, some_date, 103)
from ( select * from some_table where isdate(some_date) = 1 )
But then I added a where clause
select convert(datetime, some_date, 103)
from ( select * from some_table where isdate(some_date) = 1 )
where convert(datetime, some_date ,103) between
convert(datetime, '01/07/2014')
and convert(datetime,'31/07/2014')
How does a where clause being executed before the from?
Since we know that a valid date looks like dd/mm/yyyy how about a simple structure test?
select convert(datetime, some_date, 103)
from some_table
where len(some_date) = 10 --must have exactly the expected number of characters
This could be expanded upon (depending on requirements)
and some_date like '__/__/____'
Or maybe
and some_date like '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]'
Anything better would probably need you to put together a custom function that does the CONVERT in a try block, returning NULL in the catch block if its an invalid record. Depends on your requirements...
The problem is happening and will continue to happen because you can't be certain which order the sql engine will take here. One time it might filter the rows first and another time it does the conversion. So what you need to do is force the order the engine works. We can do this by using a CTE to get only the rows we want to convert.
with MyCTE as
(
select some_date
from some_table
where ISDATE(some_date) = 1
)
select CONVERT(datetime, some_date, 103)
from MyCTE
If you're positive that these are the only malformed values and all malformed values will look like this, you could just go ahead and use a hackish CASE statement
SELECT (CASE WHEN LEN(datetime) > 3 THEN convert(datetime, some_date, 103) ELSE NULL),
...
It's really not elegant, but datetimes are notoriously difficult to make elegant because there are so many fragmented formats. If possible, it saves more headaches just to make sure that your data is correctly formatted in the first place through form validation and regex stuff, but since that's not always possible, we have crummy hacks like this to patch ourselves through. Best of luck!

How to set date format for the result of a SELECT statement in SQL Server

I know how to use CONVERT function in SELECT statement to change the format of the Date column:
SELECT
StationID
, CONVERT(varchar, [Date], 101) as Date
, Value
FROM my_table
But I was wondering if I can set the date format in general before running the SELECT statement, when I don't know the name of the date column in the following SELECT statement:
SELECT * from FROM my_table
Is any SET statement or other T-SQL that I can run before my SELECT statement so that I can change the Date format temporarily?
Thank you
No.
In particular, any date columns which you select are not actually formatted at all, but are instead returned down the wire as an actual piece of date data in a binary "format" which is used for dates. If you are seeing them formatted, it's because your client (either management studio or some other tool) is converting them to strings to display.
When you use SELECT *, there is obviously no way to tell SQL Server to do any conversions on any particular columns, so the data is going to be returned in whatever the data types of the underlying query returns. So regardless of whether your data types are really date or not, no manipulation is going to happen at that point anyway.
I'm pretty sure there's no way to do what you're asking. However, there are ways to format the date string when you output it using your programming language.

Sql Server string to date conversion

I want to convert a string like this:
'10/15/2008 10:06:32 PM'
into the equivalent DATETIME value in Sql Server.
In Oracle, I would say this:
TO_DATE('10/15/2008 10:06:32 PM','MM/DD/YYYY HH:MI:SS AM')
This question implies that I must parse the string into one of the standard formats, and then convert using one of those codes. That seems ludicrous for such a mundane operation. Is there an easier way?
Try this
Cast('7/7/2011' as datetime)
and
Convert(DATETIME, '7/7/2011', 101)
See CAST and CONVERT (Transact-SQL) for more details.
Run this through your query processor. It formats dates and/or times like so and one of these should give you what you're looking for. It wont be hard to adapt:
Declare #d datetime
select #d = getdate()
select #d as OriginalDate,
convert(varchar,#d,100) as ConvertedDate,
100 as FormatValue,
'mon dd yyyy hh:miAM (or PM)' as OutputFormat
union all
select #d,convert(varchar,#d,101),101,'mm/dd/yy'
union all
select #d,convert(varchar,#d,102),102,'yy.mm.dd'
union all
select #d,convert(varchar,#d,103),103,'dd/mm/yy'
union all
select #d,convert(varchar,#d,104),104,'dd.mm.yy'
union all
select #d,convert(varchar,#d,105),105,'dd-mm-yy'
union all
select #d,convert(varchar,#d,106),106,'dd mon yy'
union all
select #d,convert(varchar,#d,107),107,'Mon dd, yy'
union all
select #d,convert(varchar,#d,108),108,'hh:mm:ss'
union all
select #d,convert(varchar,#d,109),109,'mon dd yyyy hh:mi:ss:mmmAM (or PM)'
union all
select #d,convert(varchar,#d,110),110,'mm-dd-yy'
union all
select #d,convert(varchar,#d,111),111,'yy/mm/dd'
union all
select #d,convert(varchar,#d,12),12,'yymmdd'
union all
select #d,convert(varchar,#d,112),112,'yyyymmdd'
union all
select #d,convert(varchar,#d,113),113,'dd mon yyyy hh:mm:ss:mmm(24h)'
union all
select #d,convert(varchar,#d,114),114,'hh:mi:ss:mmm(24h)'
union all
select #d,convert(varchar,#d,120),120,'yyyy-mm-dd hh:mi:ss(24h)'
union all
select #d,convert(varchar,#d,121),121,'yyyy-mm-dd hh:mi:ss.mmm(24h)'
union all
select #d,convert(varchar,#d,126),126,'yyyy-mm-dd Thh:mm:ss:mmm(no spaces)'
In SQL Server Denali, you will be able to do something that approaches what you're looking for. But you still can't just pass any arbitrarily defined wacky date string and expect SQL Server to accommodate. Here is one example using something you posted in your own answer. The FORMAT() function and can also accept locales as an optional argument - it is based on .Net's format, so most if not all of the token formats you'd expect to see will be there.
DECLARE #d DATETIME = '2008-10-13 18:45:19';
-- returns Oct-13/2008 18:45:19:
SELECT FORMAT(#d, N'MMM-dd/yyyy HH:mm:ss');
-- returns NULL if the conversion fails:
SELECT TRY_PARSE(FORMAT(#d, N'MMM-dd/yyyy HH:mm:ss') AS DATETIME);
-- returns an error if the conversion fails:
SELECT PARSE(FORMAT(#d, N'MMM-dd/yyyy HH:mm:ss') AS DATETIME);
I strongly encourage you to take more control and sanitize your date inputs. The days of letting people type dates using whatever format they want into a freetext form field should be way behind us by now. If someone enters 8/9/2011 is that August 9th or September 8th? If you make them pick a date on a calendar control, then the app can control the format. No matter how much you try to predict your users' behavior, they'll always figure out a dumber way to enter a date that you didn't plan for.
Until Denali, though, I think that #Ovidiu has the best advice so far... this can be made fairly trivial by implementing your own CLR function. Then you can write a case/switch for as many wacky non-standard formats as you want.
UPDATE for #dhergert:
SELECT TRY_PARSE('10/15/2008 10:06:32 PM' AS DATETIME USING 'en-us');
SELECT TRY_PARSE('15/10/2008 10:06:32 PM' AS DATETIME USING 'en-gb');
Results:
2008-10-15 22:06:32.000
2008-10-15 22:06:32.000
You still need to have that other crucial piece of information first. You can't use native T-SQL to determine whether 6/9/2012 is June 9th or September 6th.
SQL Server (2005, 2000, 7.0) does not have any flexible, or even non-flexible, way of taking an arbitrarily structured datetime in string format and converting it to the datetime data type.
By "arbitrarily", I mean "a form that the person who wrote it, though perhaps not you or I or someone on the other side of the planet, would consider to be intuitive and completely obvious." Frankly, I'm not sure there is any such algorithm.
Use this:
SELECT convert(datetime, '2018-10-25 20:44:11.500', 121) -- yyyy-mm-dd hh:mm:ss.mmm
And refer to the table in the official documentation for the conversion codes.
For this problem the best solution I use is to have a CLR function in Sql Server 2005 that uses one of DateTime.Parse or ParseExact function to return the DateTime value with a specified format.
Short answer:
SELECT convert(date, '10/15/2011 00:00:00', 101) as [MM/dd/YYYY]
Other date formats can be found at SQL Server Helper > SQL Server Date Formats
Took me a minute to figure this out so here it is in case it might help someone:
In SQL Server 2012 and better you can use this function:
SELECT DATEFROMPARTS(2013, 8, 19);
Here's how I ended up extracting the parts of the date to put into this function:
select
DATEFROMPARTS(right(cms.projectedInstallDate,4),left(cms.ProjectedInstallDate,2),right( left(cms.ProjectedInstallDate,5),2)) as 'dateFromParts'
from MyTable
The most upvoted answer here are guravg's and Taptronic's. However, there's one contribution I'd like to make.
The specific format number they showed from 0 to 131 may vary depending on your use-case (see full number list here), the input number can be a nondeterministic one, which means that the expected result date isn't consistent from one SQL SERVER INSTANCE to another, avoid using the cast a string approach for the same reason.
Starting with SQL Server 2005 and its compatibility level of 90,
implicit date conversions became nondeterministic. Date conversions
became dependent on SET LANGUAGE and SET DATEFORMAT starting with
level 90.
Non deterministic values are 0-100, 106, 107, 109, 113, 130. which may result in errors.
The best option is to stick to a deterministic setting, my current preference are ISO formats (12, 112, 23, 126), as they seem to be the most standard for IT people use cases.
Convert(varchar(30), '210510', 12) -- yymmdd
Convert(varchar(30), '20210510', 112) -- yyyymmdd
Convert(varchar(30), '2021-05-10', 23) -- yyyy-mm-dd
Convert(varchar(30), '2021-05-10T17:01:33.777', 126) -- yyyy-mm-ddThh:mi:ss.mmm (no spaces)
This page has some references for all of the specified datetime conversions available to the CONVERT function. If your values don't fall into one of the acceptable patterns, then I think the best thing is to go the ParseExact route.
Personally if your dealing with arbitrary or totally off the wall formats, provided you know what they are ahead of time or are going to be then simply use regexp to pull the sections of the date you want and form a valid date/datetime component.
If you want SQL Server to try and figure it out, just use CAST
CAST('whatever' AS datetime)
However that is a bad idea in general. There are issues with international dates that would come up. So as you've found, to avoid those issues, you want to use the ODBC canonical format of the date. That is format number 120, 20 is the format for just two digit years.
I don't think SQL Server has a built-in function that allows you to provide a user given format. You can write your own and might even find one if you search online.
convert string to datetime in MSSQL implicitly
create table tmp
(
ENTRYDATETIME datetime
);
insert into tmp (ENTRYDATETIME) values (getdate());
insert into tmp (ENTRYDATETIME) values ('20190101'); --convert string 'yyyymmdd' to datetime
select * from tmp where ENTRYDATETIME > '20190925' --yyyymmdd
select * from tmp where ENTRYDATETIME > '20190925 12:11:09.555'--yyyymmdd HH:MIN:SS:MS
You can easily achieve this by using this code.
SELECT Convert(datetime, Convert(varchar(30),'10/15/2008 10:06:32 PM',102),102)
This code solve my problem :
convert(date,YOUR_DATE,104)
If you are using timestamp you can you the below code :
convert(datetime,YOUR_DATE,104)
dateadd(day,0,'10/15/2008 10:06:32 PM')

Resources