Link to home
Start Free TrialLog in
Avatar of Jay Roy
Jay RoyFlag for United States of America

asked on

conversion of a varchar data type to a datetime

hi guys

I am using sql server 2012

This is my sql which works fine in 2008
SELECT distinct NAME,DATE FROM WAYNE_CUSTOMERS
WHERE UP_DT BETWEEN '2013-10-30 18:04:37.880' and '2013-10-30 18:04:37.897'

But in 2012 version i get this error

>>The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.

Any idea how i can fix this?
thanks.
SOLUTION
Avatar of chaau
chaau
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Jay Roy

ASKER

Thanks
Can you please tell me what is 121 in your SQL?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Do not specify a conversion, or otherwise convert to datetime yourself.  You're just risking potentially serious performance issues later.

Instead, use a 100% safe literal date format, i.e., YYYYMMDD, only:

WHERE UP_DT BETWEEN '20131030 18:04:37.880' and '20131030 18:04:37.897'
The explicit conversion of two literals leads to performance risk?  
Relying on implicit conversion produced a risk and led to this question.

I'd certainly agree that doing funky conversions on the data is a performance issue, but that's not the case here.
>> The explicit conversion of two literals leads to performance risk? <<

Not directly, but indirectly is definitely possible.  See below.


>> Relying on implicit conversion produced a risk and led to this question. <<

No, using a non-universal literal date format lead to this problem.

Why not use a format that always works, and that you don't have to explicitly convert?  Just having to lookup and use all the conversion codes is a pain anyway, leading to qs like:
"Can you please tell me what is 121 in your SQL?"

Why unnecessarily cause all that hassle?!


>> I'd certainly agree that doing funky conversions on the data is a performance issue, but that's not the case here. <<

Ah, but a forced/implicit conversion is the potential performance problem:

*) Doing the explicit conversion of the literal forces a specific data type on the result, in this case datetime:
"CONVERT(datetime, '2013-10-30 18:04:37.880', 121)"

*) If that data type has a higher precedence than the column data type it's being compared to, that might force an implicit conversion on the table column, and an implicit column conversion would prevent an index seek from being done on that column, in turn potentially causing a HUGE performance hit.  You never want SQL having to do an implicit conversion on a table column if you can avoid it.


Worse, you can even MISS rows that (arguably) SHOULD be SELECTed, because SQL didn't do an implicit conversion, but used the datetime value as you specified it ... GRRRRR.

This code demonstrates that (at least on SQL 2008, SP2):

IF OBJECT_ID('tempdb.dbo.#dates') IS NOT NULL
    DROP TABLE #dates
CREATE TABLE #dates ( up_dt date, string varchar(30) )
CREATE CLUSTERED INDEX dates__CL ON #dates ( up_dt )

;WITH
cteDigits AS (
    SELECT 0 AS digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
    SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
),
cteTally AS (
    SELECT [100000s].digit * 100000 + [10000s].digit * 10000 +
        [1000s].digit * 1000 + [100s].digit * 100 + [10s].digit * 10 + [1s].digit AS tally
    FROM cteDigits [1s]
    CROSS JOIN cteDigits [10s]
    CROSS JOIN cteDigits [100s]
    CROSS JOIN cteDigits [1000s]    
    CROSS JOIN cteDigits [10000s]
    CROSS JOIN cteDigits [100000s]
)
INSERT INTO #dates
SELECT DATEADD(DAY, tally / 20 , 0) AS up_dt, CAST(tally AS varchar(30)) AS string
FROM cteTally
WHERE
    tally > 500000
ORDER BY 1


GO

-- assume "up_dt" was originally datetime but was changed to date;
-- would you want rows being SELECTed or not!?!


-- no rows are SELECTed, even though there are 20 rows with a date of 20131030;
-- (btw, interesting that SQL doesn't force up_dt to be implicitly converted to datetime).
SELECT *
FROM #dates
WHERE up_dt = CONVERT(datetime, '2013-10-30 18:04:37.880', 121)

-- all rows are SELECTed that have a date of 20131030
SELECT *
FROM #dates
WHERE up_dt = '20131030 18:04:37.880'
Thanks Scott, most interesting.
Avatar of Jay Roy

ASKER

thanks guys

Scott,
you said
>Instead, use a 100% safe literal date format, i.e., YYYYMMDD, only
WHERE UP_DT BETWEEN '20131030 18:04:37.880' and '20131030 18:04:37.897'

just to be clear from my observation my sql (in my original question ) worked without errors in 2008 version, the problem only arises when i run it in 2012 version.

So are you saying that something like this should work fine in 2008 and 2012 versions ?
<< SELECT distinct NAME,DATE FROM WAYNE_CUSTOMERS
WHERE UP_DT BETWEEN '20131030 18:04:37.880' and '20131030 18:04:37.897'  >>

basically by removing the 'dash' from the dates ?

thanks.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial