tags:

views:

70

answers:

3

I wrote the following query to obtain a date, remove it's time part and add the time I wanted. If I run this query without the TOP clause, it works well. But when I add it, it returns the following exception: "Conversion failed when converting date and/or time from character string."

Here is the query:

SELECT TOP 1 
CONVERT(DateTime, (CONVERT(varchar(50),CONVERT(Date, VRSAS.EventOn)) 
+ ' ' + 
CONVERT(varchar(50), CONVERT(Time, '23:30')))) E 
FROM ViewRangeSheetActualStatus VRSAS 
Where VRSAS.EventOn <= '2010-07-31' 
AND VRSAS.[Status] = 1
 order by VRSAS.RangeSheet

The field EventOn is of type DateTime.

What could be going on?

+1  A: 

A strange one, have you verified that it really does work with out the "top one" restriction? Sometimes the "top one" just makes the error more visible. If you have many, many rows and you remove the "top one" restriction, the query may give the impression of working, but in the background its still spooling the results and hasn't hit the line that causes the problem.

Is EventOn non-nullable, that could be a prime reason. If so, put a non null check first.

Also, what is the type "RangeSheet", what data type is it and can that hold nulls?

Paul Hadfield
I ran the query without the TOP clause, it worked well. The column EventOn can be null, but that shouldn't be a problem because the conversion I did above will return null if the column EventOn is null, just like it does without the TOP clause.The row found in this TOP 1 clause has a valid date on the EventOn field.RangeSheet is a parentId, and it is non-nullable.
Hallaghan
OK, could you change the query to just select TOP 1 EventOn and let us know the exact value it returns. Also run both bits of the query in isolation to see what causes the problem. So same query, but just TOP 1 (CONVERT(varchar(50),CONVERT(Date, VRSAS.EventOn)) and then again but with just CONVERT(varchar(50), CONVERT(Time, '23:30'))))
Paul Hadfield
The first bit of it returns this: 2010-02-08The second bit of it returns this:23:30:00.0000000The full query without Convert returns this: 2010-02-08 08:20:57.017
Hallaghan
@Hallaghan - thanks for the info, sorry I could help you, @Martin's answer is a nice clean solution. I think the issue with your code is either ydm / ymd issue as martin 1st said or an issue with how the date is being represented as a string and then cast back. Pre-SQL2008 in a past company we had a function once called floor that just stripped off the time using CAST(Year(date) AS VARCHAR(4)) + " " MonthName(Month(Date)) + " " CAST(Day(Date)) AS VARCHAR(2)). That got around a lot of these cast issues but seemed a lot of effort just to remove the time!
Paul Hadfield
+2  A: 

I've reproduced quite easily this end. I found using DATEADD resolved it

DATEADD(MINUTE, 23*60 + 30, CONVERT(DATETIME,CONVERT(DATE, VRSAS.EventOn)))

But I'm not actually sure why yet. Steps to reproduce below.

CREATE TABLE ViewRangeSheetActualStatus
(EventOn DATETIME,
[Status] BIT,
RangeSheet INT
)

INSERT INTO [dbo].[ViewRangeSheetActualStatus]([EventOn], [Status], [RangeSheet])
SELECT '20100903 11:02:39.517', 1, 1 UNION ALL
SELECT '20100731 11:03:23.577', 1, 1 UNION ALL
SELECT '20100731 00:00:00.000', 1, 1

/*Selects ALL records - No error*/
SELECT 
CONVERT(DateTime, (CONVERT(varchar(50),CONVERT(Date, VRSAS.EventOn)) 
+ ' ' + 
CONVERT(varchar(50), CONVERT(Time, '23:30')))) E 
FROM ViewRangeSheetActualStatus VRSAS 

/*Selects top (1) record - Error!*/   
SELECT top (1)
CONVERT(DateTime, (CONVERT(varchar(50),CONVERT(Date, VRSAS.EventOn)) 
+ ' ' + 
CONVERT(varchar(50), CONVERT(Time, '23:30')))) E 
FROM ViewRangeSheetActualStatus VRSAS 

Looking at the ComputeScalar properties in the execution plan the two are different.

All

(CONVERT(datetime,(CONVERT(varchar(50),CONVERT(date,[ViewRangeSheetActualStatus].
 [EventOn] as [VRSAS].[EventOn],0),0)+[@1])+CONVERT(varchar(50),CONVERT(time(7), 
 [@2],0),0),0))

Top 1

(CONVERT(datetime,(CONVERT(varchar(50),CONVERT(date,[ViewRangeSheetActualStatus].
 [EventOn] as [VRSAS].[EventOn],0),121)+' ')+'23:30:00.0000000',0))

Before the final conversion to datetime the first one produces a varchar containing the following

------------------------------
Sep  3 2010 11:30PM
Jul 31 2010 11:30PM
Jul 31 2010 11:30PM

The second version produces a varchar containing

------------------------------
2010-09-03 23:30:00.0000000

It is the .0000000 that causes the problem casting back to datetime. I have no idea why the addition of TOP to the query would cause this completely unrelated change in behaviour.

Martin Smith
I tried what you suggested Martin, but it returns the same error.
Hallaghan
@Hallaghan - Sorry you would need to change the WHERE clause to this as well. `Where VRSAS.EventOn <= '20100731'` so that it doesn't begin to cause an error when you switch date formats.
Martin Smith
Martin, once again you helped me out just wonderfully! Your DATEADD use in the query above fixed the problem and is a much more elegant and correct way of doing things. Thanks for this, you motivate me to learn more and become better.
Hallaghan
@Hallaghan - I'm not sure that my dateformat explanation was correct though. I've managed to reproduce quite easily this end and will post my results if I figure it out!
Martin Smith
@Martin, is it because when selecting all the values it knows the date format as it gets values of 31, so knows that is the day column. When you select top one that processing doesn't happen, so it gets its days and months mixed up, causing a problem. I can't really see how it happens but it's the only way that makes sense.
Paul Hadfield
@Paul - No that's not the reason. See my edit.
Martin Smith
@Martin - I just read your complete edit and although I understood what you showed me, it is confusing why the TOP clause changes the format type. As for the part before the final conversion to datetime, I had seen it before myself when trying to figure out why my query wasn't working. I tried doing it in many ways until I had finally removed top and seen it working. Once again, thanks for your help and this nice discussion.
Hallaghan
@Hallaghan - I've not come across this before either. I'm not sure if it is documented behaviour or not. Definitely most unexpected!
Martin Smith
@Martin - wow, good find, definitely a bit strange!
Paul Hadfield
+1  A: 

It seems that auto-parameterization is to blame for the inconsistency.

Books Online documents that DATE, TIME, DATETIME2, and DATETIMEOFFSET use CONVERT style 121 by default, whereas style 0 is used for DATETIME and SMALLDATETIME. Someone forgot to update the auto-parameterization rules for the new types :)

Where the query can be auto-parameterized, style 0 is erroneously applied to the new DATE/TIME types if an implicit conversion, or an explicit conversion with a specified style takes place. The query without TOP is auto-parameterized (parameter [@1] appears instead of the TIME literal). TOP is one of the (many) query features that prevents auto-parameterization.

The obvious workaround is to always specify a desired style when using CONVERT.

Paul White NZ