Reputation: 142
I've got an issue while executing a SQL statement in SQL Server 2012 while it's perfectly working in a SQL Server 2008 R2...
The error message is :
Msg 8114, Level 16, State 5, Line 1
Error converting data type varchar to numeric.
I want to execute this SQL Select statement :
Select
count(*)
from IMPORTBM
inner join ATTRIBUTE on ATT_ATTRIBUTE_ID = IMP_ATTRIBUTE_ID
where IMP_LOCATION_ID = 2
AND IMP_SERIAL_ID = 310001
AND IMP_VERSION_ID = 1
AND (
(ATT_ATTRIBUTE = 'PS_APISizing'
AND IMP_VALUE = 'C')
OR
(ATT_ATTRIBUTE = 'DTD'
AND ISNUMERIC(IMP_VALUE) = 1
AND CAST( IMP_VALUE as NUMERIC(38,19)) <= 0.469)
OR
(ATT_ATTRIBUTE = 'IOD'
AND ISNUMERIC(IMP_VALUE) = 1
AND CAST( IMP_VALUE as NUMERIC(38,19)) BETWEEN 3.684 AND 4.225)
)
Could you help me finding out why it's not working with SQL Server 2012 please ?
Upvotes: 4
Views: 1311
Reputation: 3952
What is the definition of the table? (IMP_VALUE at least). Why not store numeric in the 1st place... 2008 and 2012 query plan probably differ and does not handle the parameter and condition in the same order.
With SQL Server 2012 (compatibility level 110) you can try to replace the 2 cast:
AND ISNUMERIC(IMP_VALUE) = 1
AND CAST( IMP_VALUE as NUMERIC(38,19)) <= 0.469)
by
AND TRY_CONVERT(NUMERIC(38,19), IMP_VALUE) <= 0.469
AND TRY_CONVERT(NUMERIC(38,19), IMP_VALUE) BETWEEN 3.684 AND 4.225
It returns NULL without error when it cannot convert it.
Upvotes: 1
Reputation: 50251
The problem is that conditions are not always evaluated in the order you place them. This means that Cast(IMP_VALUE as numeric(38, 19))
can fail when it is a non-numeric value, if the query execution plan happens to evaluate this before the IsNumeric(IMP_VALUE) = 1
.
This is not determined by how many clauses there are or what version of SQL Server you're running--those are only coincidences. The problem is exactly as I described. The fact that you get different results with different queries is due to different queries, or different servers, using different execution plans.
The cure is to make sure that all conditions will not throw an error, no matter what order they are executed in.
For all versions of SQL Server, you can do this:
Convert(
numeric(38,19),
CASE WHEN IsNumeric(IMP_VALUE) = 1 THEN IMP_VALUE ELSE NULL END
) <= 0.469
Or, in SQL Server 2012 and up, you can use Try_Convert:
Try_Convert(numeric(38,19), IMP_VALUE) <= 0.469
If your server complains that this is not a built-in function name, then it is likely that your database was upgraded from a prior version and you need to alter the database compatibility level. Note that doing this could have some other effects in the system that you need to consider, so study up on it first, and try it out in a non-production system before doing it in production (or do it during a non-critical period and set the compatibility level back if you run into any issues). The danger is not that any damage will occur, but that query results could be different or errors could occur (different ordering of query or stored procedure results being a more common occurrence).
ALTER DATABASE database_name
SET COMPATIBILITY_LEVEL = 110; -- SQL Server 2012
Upvotes: 2