Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL Server : error converting data type varchar to numeric

Tags:

I have a table:

Account_Code | Desc 503100       | account xxx 503103       | account xxx 503104       | account xxx 503102A      | account xxx 503110B      | account xxx 

Where Account_Code is a varchar.

When I create a query below:

Select    cast(account_code as numeric(20,0)) as account_code,   descr  from account  where isnumeric(account_code) = 1 

It runs well by returning all record that have a valid numeric value in account_code column.

But when I try to add another select, nested to prior sql:

select account_code,descr  from  (   Select cast(account_code as numeric(20, 0)) as account_code,descr    from account    where isnumeric(account_code) = 1 ) a  WHERE account_code between 503100 and 503105 

the query will return an error

Error converting data type varchar to numeric.

What is happening there?

I have already converted to numeric if account_code valid, but it seems the query is still trying to process a non valid record.

I need to use BETWEEN clause in my query.

like image 304
user1947840 Avatar asked Jan 04 '13 08:01

user1947840


People also ask

Can you convert varchar to numeric in SQL?

To convert a varchar type to a numeric type, change the target type as numeric or BIGNUMERIC as shown in the example below: SELECT CAST('344' AS NUMERIC) AS NUMERIC; SELECT CAST('344' AS BIGNUMERIC) AS big_numeric; The queries above should return the specified value converted to numeric and big numeric.

Can we convert char to int in SQL?

Converting from one data type to another is a common operation you'll perform when working in databases. And SQL provides a useful utility known as CAST to achieve this. We'll see how it works in this article.

How do I convert int to numeric in SQL?

Use the CAST() function to convert an integer to a DECIMAL data type. This function takes an expression or a column name as the argument, followed by the keyword AS and the new data type. In our example, we converted an integer (12) to a decimal value (12.00).


2 Answers

SQL Server 2012 and Later

Just use Try_Convert instead:

TRY_CONVERT takes the value passed to it and tries to convert it to the specified data_type. If the cast succeeds, TRY_CONVERT returns the value as the specified data_type; if an error occurs, null is returned. However if you request a conversion that is explicitly not permitted, then TRY_CONVERT fails with an error.

Read more about Try_Convert.

SQL Server 2008 and Earlier

The traditional way of handling this is by guarding every expression with a case statement so that no matter when it is evaluated, it will not create an error, even if it logically seems that the CASE statement should not be needed. Something like this:

SELECT    Account_Code =       Convert(          bigint, -- only gives up to 18 digits, so use decimal(20, 0) if you must          CASE          WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL          ELSE X.Account_Code          END       ),    A.Descr FROM dbo.Account A WHERE    Convert(       bigint,       CASE       WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL       ELSE X.Account_Code       END    ) BETWEEN 503100 AND 503205 

However, I like using strategies such as this with SQL Server 2005 and up:

SELECT    Account_Code = Convert(bigint, X.Account_Code),    A.Descr FROM    dbo.Account A    OUTER APPLY (       SELECT A.Account_Code WHERE A.Account_Code NOT LIKE '%[^0-9]%'    ) X WHERE    Convert(bigint, X.Account_Code) BETWEEN 503100 AND 503205 

What this does is strategically switch the Account_Code values to NULL inside of the X table when they are not numeric. I initially used CROSS APPLY but as Mikael Eriksson so aptly pointed out, this resulted in the same error because the query parser ran into the exact same problem of optimizing away my attempt to force the expression order (predicate pushdown defeated it). By switching to OUTER APPLY it changed the actual meaning of the operation so that X.Account_Code could contain NULL values within the outer query, thus requiring proper evaluation order.

You may be interested to read Erland Sommarskog's Microsoft Connect request about this evaluation order issue. He in fact calls it a bug.

There are additional issues here but I can't address them now.

P.S. I had a brainstorm today. An alternate to the "traditional way" that I suggested is a SELECT expression with an outer reference, which also works in SQL Server 2000. (I've noticed that since learning CROSS/OUTER APPLY I've improved my query capability with older SQL Server versions, too--as I am getting more versatile with the "outer reference" capabilities of SELECT, ON, and WHERE clauses!)

SELECT    Account_Code =       Convert(          bigint,          (SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%')       ),    A.Descr FROM dbo.Account A WHERE    Convert(       bigint,       (SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%')    ) BETWEEN 503100 AND 503205 

It's a lot shorter than the CASE statement.

like image 66
ErikE Avatar answered Sep 24 '22 14:09

ErikE


There's no guarantee that SQL Server won't attempt to perform the CONVERT to numeric(20,0) before it runs the filter in the WHERE clause.

And, even if it did, ISNUMERIC isn't adequate, since it recognises £ and 1d4 as being numeric, neither of which can be converted to numeric(20,0).(*)

Split it into two separate queries, the first of which filters the results and places them in a temp table or table variable, the second of which performs the conversion. (Subqueries and CTEs are inadequate to prevent the optimizer from attempting the conversion before the filter)

For your filter, probably use account_code not like '%[^0-9]%' instead of ISNUMERIC.


(*) ISNUMERIC answers the question that no-one (so far as I'm aware) has ever wanted to ask - "can this string be converted to any of the numeric datatypes - I don't care which?" - when obviously, what most people want to ask is "can this string be converted to x?" where x is a specific target datatype.

like image 35
Damien_The_Unbeliever Avatar answered Sep 24 '22 14:09

Damien_The_Unbeliever