Date conversion doesn't work in where clause - sql

I am trying to query against the columns SOE (datetime field) and Answer (varchar field) from a table where NOT ALL Answer values are dates but some of them are. If I remove the datediff from the 'where' clause, I'm able to run the query just fine. But on including it, it errors out saying that 'Conversion failed when converting date and/or time from character string'.
For context, I'm using SQL Server 2014.
This is what my query looks like:
select ID,
convert(datetime, Answer, 121) as Answer,
datediff(dd,convert(datetime,
Answer, 121), SOE) as Days
from table
where Type in (1) and SOE between '2019-01-01' and '2019-03-31'
and FormLocation = 'M1005_INP_DISCHARGE_DT'
and PayorType = 'Medicare'
and Answer <> ' '
datediff(dd, convert(datetime, Answer, 121), SOE) <= 5
Any tips on how to resolve this would be appreciated.

Before doing the conversion to datetime make sure the data in the "Answer" field can be converted to date. Try this...
and ((isdate(Answer) = 1) and (datediff(dd, convert(datetime, Answer, 121), SOE) <= 5))

Since 2012 you can use try_convert().
...
datediff(dd, try_convert(datetime, Answer, 121), SOE) <= 5
...
try_convert() returns NULL if the conversion doesn't succeed. And so does datediff() then. So you might want to handle that case some way.

Using apply you can save yourself some repetitive code:
select ID,
answer_datetime as Answer,
datediff(day, answer_datetime, SOE) as Days
from table t cross apply
(values (try_convert(datetime, Answer, 121))) v(answer_datetime)
where Type in (1) and
SOE between '2019-01-01' and '2019-03-31' and
FormLocation = 'M1005_INP_DISCHARGE_DT' and
PayorType = 'Medicare' and
Answer <> ' ' and
datediff(day, answer_datetime, SOE) <= 5;
But you should really fix the data definitions so date/times are stored using the correct type.

Related

Effective conversion of strings with date "31. 12. 2019" and "7/2020" to the date type?

I am importing CSV files that contain dates in two alternative forms like:
31. 12. 2019
7/2020
The two forms may alternatively occur in the same column; so, the part of the solution have to be detection of the form.
As the second form 7/2020 does not contain the day information, I am using 1 for the day.
So far, I have worked only with the subset of records that used predictable form of the used date. I have followed the T-SQL documentation for the CONVERT() function (https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql). Let's assume #date = '31. 12. 2019' then the following conversion is done:
CONVERT(date, #date, 104) AS my_date -- the German form
For the #date = '7/2020' I do the following:
CONVERT(date, '1/' + #date, 103) AS my_date -- the British/French form
Now, if I do not know (in advance) what form the #date contains, how to convert it?
Actually, the date is not in a #date variable. Instead, it is the field value in the SELECT query -- say xdate. I expect the code to look like:
CASE WHEN /* detect the British/French case */ THEN CONVERT(date, '1/' + xdate, 103)
WHEN /* detect the German case */ THEN CONVERT(date, xdate, 104)
ELSE NULL -- this does not happen, anyway...
END AS my_date
... but there may be some better way.
If I understand the question correctly, a possible solution is a combination of COALESCE() and TRY_CONVERT():
Statement:
SELECT COALESCE(
TRY_CONVERT(date, xdate, 104),
TRY_CONVERT(date, '1/' + xdate, 103)
) AS xdate
FROM (VALUES
('31. 12. 2019'),
('7/2000'),
('wrong date 12/12')
) v (xdate)
Result:
xdate
----------
2019-12-31
2000-07-01
null
A simple charindex can get you an indication of the string contains a slash (/) or not - which is a good enough indication if the text format is m/yyyy or dd. mm. yyyy.
However, you should use Try_convert and not convert because a case expression isn't guaranteed to short-circuit, meaning all when clauses might be evaluated.
Here's how I would write it:
SELECT CASE WHEN CHARINDEX('/', xdate) > 0 THEN TRY_CONVERT(date, '1/' + xdate, 103)
ELSE TRY_CONVERT(date, xdate, 104)
END As my_date
I take it you're dumping the CSV into a table using bulk insert or some similar process, and the column in that table storing the unprocessed date is a varchar, and then you're running a query against the table to process the contents, yeah?
If that's the case, and there are only ever those two forms, you could use like, or charindex(). The performance is going to be pretty much the same as far as I know, so do whichever seems easier to read. Personally I'd probably use charindex:
select case
when charindex('/', xdate) > 0 then convert(date, '1/' + xdate, 103)
else convert(date, xdate, 104)
end
Or using like:
select case
when xdate like '%/%' then convert(date, '1/' + xdate, 103)
else convert(date, xdate, 104)
end
If those aren't the only two forms then you probably want to try_convert instead, or just cover all of your bases

Convert a varchar YYYYMMDD into datetime to compare with GETDATE()

What am I doing wrong here? I've looked through other posts but I'm getting different results than other people.
Trying to convert a varchar YYYYMMDD to datetime, and I keep getting:
Msg 8115, Level 16, State 2, Line 2
Arithmetic overflow error converting expression to data type datetime.
Attempts:
CONVERT(DATETIME, EXPDATE)
CONVERT(DATETIME, EXPDATE, 102)
(CONVERT(DATETIME, CAST(EXPDATE AS CHAR(8)), 112))
CONVERT(DATETIME, CAST(expdate AS VARCHAR(8)))
Am I bungling something obvious here?
You clearly have a values that are not valid dates.
In SQL Server 2012+, I would suggest TRY_CONVERT():
TRY_CONVERT(DATETIME, EXPDATE)
Then, look at the values that are NULL to see where data problems may be.
In earlier versions, you should be able to use isdate():
(CASE WHEN ISDATE(EXPDATE) = 1 THEN CAST(EXPDATE AS DATE) END)
I have a similarly formatted date field (DateKey) in a table I use. I convert it to DATETIME using this syntax:
SELECT CAST(CAST(DateKey AS VARCHAR(10)) AS DATETIME) FROM tblDate
Will this work for you?
Excellent. Gordon and chancrovsky, you helped me flush out the values that were formatted incorrectly. I used a version of what you posted and this worked
(CASE WHEN ISDATE(ExpDate) = 1 THEN CAST(ExpDate AS DATE) ELSE NULL
END) as ExpDate
Thank you so much!

Is there an equivalent to sysdate-1/24 in SQL server?

I've tried looking around for this but I can't find an answer that seems to work with the code I'm using. Basically, the below query searches on any result from the current date. I'm trying to make it so it will search only on the last hour.
In oracle I could do this using sysdate-1/24, is there a simple equivalent within SQL server? Bearing in mind I'm already using cast to get the current sysdate.
Select distinct m_record_server
from search_recording_file1
where tr_date_recorded >= cast(convert(varchar(10), getdate(), 110) as datetime)
and m_record_server is not null
You can use:
getdate() - 1.0/24
SQL Server allows you to use such arithmetic on datetime.
The more common way would be:
dateadd(hour, -1, getdate())
In your query, you do not need to cast to a string at all:
Select distinct m_record_server
from search_recording_file1
where tr_date_recorded >= getdate() - 1.0/24 and m_record_server is not null;
If you want the date that was there one hour ago (which seems to be the intent of the code, if not the rest of the question):
Select distinct m_record_server
from search_recording_file1
where tr_date_recorded >= cast(getdate() - 1.0/24 as date) and m_record_server is not null;

SQL Datetime Convert Error

I have have an SQL Statement which returns following error:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
SELECT * FROM eBV_Platz
WHERE (ADRID = 4436) AND (ID <> 5) AND (Status = 1)
AND (CONVERT(DATETIME, '01.03.2014', 102) >= PlaceFrom)
AND (CONVERT(DATETIME, '01.03.2014') <= PlaceTo)
OR (CONVERT(DATETIME, '31.03.2014') >= PlaceFrom)
AND (CONVERT(DATETIME, '31.03.2014') <= PlaceTo)
But this one works fine and the only difference are the date values:
SELECT * FROM eBV_Platz
WHERE (ADRID = 4436) AND (ID <> 5) AND (Status = 1)
and (CONVERT(DATETIME, '01.01.2000', 102) >= PlaceFrom)
AND (CONVERT(DATETIME, '01.01.2000') <= PlaceTo)
OR (CONVERT(DATETIME, '01.06.2001') >= PlaceFrom)
AND (CONVERT(DATETIME, '01.06.2001') <= PlaceTo)
I really don't understand this. Can anybody help me?
I'm betting that the second one does not work fine, rather it converts your dates to january 06 and january 01.
You need to give it a hint that you are using a day month year format.
Try instead:
(CONVERT(DATETIME, '31.03.2014', 103)
The 103 (from MSDN) interprets the date as dd/mm/yy
As #AlexK noted in the comments, these dont really need to be converted. You could simply use the strings as long as they were in a better format.
My assumption here is that you are using MSSQL. For a different platform, the syntax would be different.
As stated in the comments, use ISO 8601 date format to specify dates (yyyy-mm-ddThh:mm:ss[.mmm]).
This would change '31.03.2014' to '2014-03-31T00:00:00.000' and remove any ambiguity.

Sybase date comparison - Correct format?

I'm pretty new to Sybase and am writing a query to return results after a specified date, and also before a specified date. MM/DD/YYYY format
At the moment im doing..
SELECT *
From aTable
WHERE afterDate >= 08/07/2013
AND beforeDate <= 08/08/2013
I'm getting records back, but as I'm a Sybase newbie, I want to be sure Sybase is interpreting these dates correctly..
Their online doc is pretty bad for basic explanations on things like this!
Anyone able to confirm if what I have works, or does it need some formatting round the dates?
You'll need to convert the dates into DATETIME and tell sybase what the format is to be sure.
According to this documentation the code for MM/DD/YYYY is 101, so something like this:
SELECT *
FROM aTable
WHERE afterDate >= CONVERT(DATETIME,'08/07/2013',101)
AND beforeDate <= CONVERT(DATETIME,'08/08/2013',101)
You can see the difference by running the following select statements:
SELECT CONVERT(DATETIME,'08/07/2013',101) --MM/DD/YYYY (2013-08-07 00:00:00.000)
SELECT CONVERT(DATETIME,'08/07/2013',103) --DD/MM/YYYY (2013-07-08 00:00:00.000)
For any date-time field in sybase, instead of going through the convert function, there is a more direct approach.
SELECT *
From aTable
WHERE afterDate >= '2013-08-07'
AND beforeDate <= '2013-08-08'
The date has to be in the form 'YYYY-MM-DD'
If you want to add a time, it can be included along with the date. The date and the time have to be separated by a T.
Any date time field can be directly used using the format 'YYYY-MM-DDTHH:MM:SS'
Using the functions is too lengthy. Noone needs a bazooka to shoot a squirrel! :)
CAST( '2000-10-31' AS DATE )
will convert from text to date format....
I am assuming that your two fields (afterDate and beforeDate) are in Date format.
Your example would be:
SELECT *
From aTable
WHERE afterDate >= CAST( '08/07/2013' AS DATE )
AND beforeDate <= CAST( '08/08/2013' AS DATE )
Also, usually (but not always) a date range is on the SAME field. As I said, that is not true all the time and you may have a good reason for that.
The best approach is to use the ANSI standard which does not require any conversion: yyyymmdd (you can also include hh:mm:ss) for instance:
DateField1 >= "20150101" and DateFile1 <= "20150102"
You should decide which Input-Strings the user is going to use as parameter and then convert them and concatenate them like you want, unless it is Datetime it is not important which initial format it had, you can use it in a between-condition.
E. g. the user is from Europe and uses "DD.MM.YY" and "hh:mm" as an input parameter, I would convert and concatenate like this:
WHERE dateCol between convert(DATETIME,
convert(char(11),
convert(DATETIME, '01.06.14', 4), 16) || ' ' || '00:00', 8)
AND convert(DATETIME,
convert(char(11),
convert(DATETIME, '01.07.14', 4), 16) || ' ' || '16:00', 8)