3

I'm trying to convert my database fields from VARCHAR(4) to FLOAT. Some of the values in these fields might not be digits since these fields didn't have any validation prior. My main target is to convert any integer or decimal value in float format and save in new database field. For this process I use INSERT SELECT STATEMENT from old table into the new table. So far I have this line of code for my conversion:

CASE WHEN LEN(LTRIM(RTRIM(hs_td2))) <> 0 AND ISNUMERIC(hs_td2) = 1 THEN CAST(LTRIM(RTRIM(hs_td2)) AS float) ELSE NULL END AS hs_td2

First step I trim the value then check if it's numeric and then convert to float otherwise set to NULL. With the code above I'm getting this error message in Microsoft Studio:

Msg 8114, Level 16, State 5, Line 13
Error converting data type varchar to float.

Line 13th is beginning of my SELECT statement. Then I tried this conversion as well:

CASE WHEN LEN(LTRIM(RTRIM(hs_td2))) <> 0 AND ISNUMERIC(hs_td2) = 1 THEN CONVERT(FLOAT, LTRIM(RTRIM(hs_td2))) ELSE NULL END AS hs_td2

and I got the same error message. Values in my fields could be something like this:

10 or 5 or -10 or 0.9 or 11.6 or -11.89 and so on...

I'm wondering if isNumeric() is the best function that I should use and why my code produces the error message listed above?

If anyone can help please let me know. Thank you!

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
espresso_coffee
  • 5,980
  • 11
  • 83
  • 193

3 Answers3

2

No, ISNUMERIC is not the best function to use.

Essentially, this question has been asked before, though not in this wording:

Try_Convert for SQL Server 2008 R2

The most upvoted answer recommends to cast to XML to use XML-specific casting function:

DECLARE @T TABLE (v varchar(4));

INSERT INTO @T (v) VALUES
('1g23'),
('-1.8'),
('11.6'),
('akjh'),
('.'),
('-'),
('$'),
('12,5');


select
    cast('' as xml).value('sql:column("V") cast as xs:float ?', 'float') as new_v
from @T

I'll leave my first version of the answer below.

Most likely you are getting the conversion error because the server tries to run CAST(LTRIM(RTRIM(hs_td2)) AS float) for each row of the table, not only for those that are numeric.

This usually happens when you try to filter out non-numeric rows using the WHERE ISNUMERIC(...) = 1 filter. Technically it may happen in CASE expression as well.

That's why they added TRY_CONVERT in 2012.

I'd try to write my own user-defined function that uses TRY-CATCH and tries to convert the given value. Yes, it will be slow.


Having said that, the example below with CASE runs fine:

DECLARE @T TABLE (v varchar(4));

INSERT INTO @T (v) VALUES
('123'),
('-1.8'),
('11.6'),
('akjh'),
('123');

SELECT
    CASE WHEN ISNUMERIC(v) = 1 THEN CAST(v AS float) ELSE NULL END AS new_v
FROM @T;

Result

+-------+
| new_v |
+-------+
| 123   |
| -1.8  |
| 11.6  |
| NULL  |
| 123   |
+-------+

But, if I put a . or - or $ value, like so:

INSERT INTO @T (v) VALUES
('123'),
('-1.8'),
('11.6'),
('akjh'),
('$');

The query fails:

Error converting data type varchar to float.

There may be other special characters and their combinations that ISNUMERIC would not complain about. That's why I originally said that overall, ISNUMERIC is not the best function to use.

If it is a one-off conversion, you can try to build a LIKE expression to catch all special cases that are present in your data, but if you need a reliable generic solution, upgrade to 2012+ and use TRY_CONVERT or write your T-SQL UDF, or your CLR UDF.

Vladimir Baranov
  • 31,799
  • 5
  • 53
  • 90
  • Well for some reason my statement is failing... I see few rows in SQL Studio and then error message shows. I'm not sure which value is causing statement to fail. – espresso_coffee Sep 18 '17 at 13:37
  • This code will fail for smth like this: INSERT INTO @T (v) VALUES ('12,4') – sepupic Sep 18 '17 at 13:52
  • @sepupic, yes, `ISNUMERIC` is not the best function to use as I said from the start. – Vladimir Baranov Sep 18 '17 at 13:54
  • @espresso_coffee, if you have the problem with ',', just add condition on it like this: SELECT CASE WHEN ISNUMERIC(v) = 1 and v not like '%,%' THEN CAST(v AS float) ELSE NULL END AS new_v FROM @T; – sepupic Sep 18 '17 at 13:54
  • @Vladimir Baranov We are now trying to catch the characters that provoke the error. If we are fortunate and it's just a comma, we'll fix it by adding a condition like in my comment above – sepupic Sep 18 '17 at 13:56
  • @sepupic, I found that this has been answered before. I've added a link. – Vladimir Baranov Sep 18 '17 at 14:06
1

Sqlxml has enough power to make magic. Of course, the performance is the problem. But still better, than million of conditions

DECLARE @T TABLE (v varchar(4));

INSERT INTO @T (v) VALUES
('123'),('-1.8'),('11.6'),('akjh'),('$'),('-.'),('-.1'),(NULL);

declare @Xml xml = (SELECT v  FROM @T T for xml auto,elements);

select T.v.value('v[1]','varchar(4)') v, T.v.value('max(v[1])','float') converted_v 
from @xml.nodes('/T') T(v);
vitalygolub
  • 735
  • 3
  • 16
0

It depends on the values in your varchar columns

ISNUMBER() for vaule such as '.' and '-' will return 1, however, it will failed when you CAST to FLOAT

ISNUMBER() for value such as '3D2' , '1e2' will return 1, and can be CAST to FLOAT, however, you may not want consider it as number.

You may try the following to convert

CASE WHEN 
  not LTRIM(RTRIM(hs_td2))like '%[^0-9.,-]%'    -- Value only contains 0-9 . -
  and LTRIM(RTRIM(hs_td2)) not like '.'          -- can not be only .
  and LTRIM(RTRIM(hs_td2)) not like '-'          -- can not be only -
  and isnumeric(LTRIM(RTRIM(hs_td2))) = 1  
THEN CAST(LTRIM(RTRIM(hs_td2)) AS float) 
ELSE NULL 
END
EricZ
  • 6,065
  • 1
  • 30
  • 30