Why ISNumeric() Transact-SQL function treats some var-chars as Int [duplicate] - sql

This question already has answers here:
CAST and IsNumeric
(11 answers)
Closed 4 years ago.
SELECT some_column
FROM some_table
WHERE some_column = '3.'
Return row/rows
SELECT some_column
FROM some_table
WHERE ISNUMERIC(some_column) = 0
AND some_column IS NOT NULL
AND some_column <> ''
Does not return any non numeric row/rows. there is a row which has a column value of '3.'
Am i missing something. Please Advise.

I don't understand the question. ISNUMERIC('3.') returns 1. So it would not be returned by the second query. And, presumably, no other rows would either.
Perhaps you really intend: somecolumn not like '%[^0-9]%'. This will guarantee that somecolumn has only the digits from 0-9.
In SQL Server 2012+, you can also use try_convert(int, somecolumn) is not null.

ISNUMERIC() has some flaws. It can return True/1 for values that are clearly not numbers.
SELECT
ISNUMERIC('.'),
ISNUMERIC('$')
If this is causing you issues, try using TRY_PARSE()
SELECT
TRY_PARSE('2' AS INT)
You can use this and filter for non-null results.
SELECT some_column
FROM some_table
WHERE TRY_PARSE(some_column AS INT) IS NOT NULL

ISNUMERIC is a legacy function, I would don't like it personally. It does exactly what it's supposed to do which is usually not what you need ("Is Numeric" is a very subjective question in Computer Science.) I recently wrote this query to clarify things for some co-workers:
SELECT string = x, [isnumeric says...] = ISNUMERIC(x)
FROM (VALUES ('1,2,3,,'),(',,,,,,,,,,'),(N'﹩'),(N'$'),(N'¢'),('12,0'),(N'52,3,1.25'),
(N'-4,1'),(N'56.'),(N'5D105'),('1E1'),('\4'),(''),(N'\'),(N'₤'),(N'€')) x(x);
Returns:
string isnumeric says...
---------- -----------------
1,2,3,, 1
,,,,,,,,,, 1
﹩ 0
$ 1
¢ 0
12,0 1
52,3,1.25 1
-4,1 1
56. 1
5D105 1
1E1 1
\4 1
0
\ 1
₤ 1
€ 1
TRY_CAST or TRY_CONVERT, or WHERE somecolumn not like '%[^0-9]%' as Gordon said, could be a good alternative.
For performance reasons it might not be a bad idea to pre-aggregate, persist and index the column by adding a new computed column. E.g. something like
ALTER <your table>
ADD isGoodNumber AS (ABS(SIGN(PATINDEX('%[^0-9]%',<your column>))-1)
This would return a 1 for rows only containing digits or a 0 otherwise. You can then index isGoodNumber (you pick a better name) for better performance.

Related

Snowflake if String Pattern replace

I have column which should only contain numbers[0-9], But in some cases we started see alphanumeric[eg:p8eSadfghrc] values in that column.
I wanna write a condition where if the value is not completely numeric{0-9}, i wanna replace it with another value from another column.
Something like this?
update t
set col = <other value>
where regexp_like(col, '[^0-9]');
This updates the data. You could also just do this in a query:
select t.*,
(case when regexp_like(col, '[^0-9]') then <other value> else col end)
from t;
In Snowflake, I would recommend try_to_decimal(). It attempts to convert the number to a decimal value (you control the target precision and scale with arguments) and rturns null if that fails:
select t.*, case when try_to_decimal(mycol) is null then myothercol else mycol end newcol
from mytable
If you want an update statement:
update mytable
set mycol = myothercol
where try_to_decimal(mycol) is null
When given no second and third argument, the number of allowed digits is 38, with 0 decimals (which seems to be what you want).

Teradata - joining char with varchar

I have two tables A and B. And I want to join A with B on A.col_1 = B.col_2. col_1 has datatype VARCHAR(35) while col_2 has datatype CHAR(35). The following statement caused problem while joining the two tables: no record returned, which mean the two tables cannot be joined. col_1 usually has 8 - 11 digits, the same with col_2. My understanding is the even I used "LENGTH(B.col_2 )-1" but the trailing spaces should not be problems as long as the values of col_1 and col_2 are the same.
What is causing this issue?
ON A.col_1 =SUBSTR(B.col_2 ,1,LENGTH(B.col_2 )-1)
Thanks!
I guess B is the char.
This will explain you what happens here:
select char_length(cast('abc' as char(10)));
10
Your substr does not take the real length of the char string but the padded length, therefore you get the original string minus 1 space.
In order to solve the issue use -
SUBSTR(B.col_2 ,1,LENGTH(cast(B.col_2 as varchar(35))-1)
or
SUBSTR(B.col_2 ,1,LENGTH(rtrim(B.col_2)-1)
... and yes, char/varchar does not matter for comparison
select 1 where cast('abc' as varchar(10)) = cast('abc' as char(10))
1

SQL Server : SUM () for only numeric values, ignoring Varchar values

I need some help as I am learning SQL so I am at a beginner level. I am using SQL Server 2008 R2.
My question is: I want to (add) sum up a column that have mix values of varchar and decimal. I want to ignore the varchar values and sum only decimal. My column is like this:
Column1
-------
0.1
Z
0.4
2.1
2.1
Z
And I need the sum that is in this case it should be: 4.7
I tried using CASE but I failed to solve it
SELECT
SUM(CASE
WHEN ISNUMERIC(Column1) = 1
THEN .(if true how i add them?).
ELSE .(and if false how i ignore them?).
END) AS Total
FROM
TABLE_Name
I am not good in explaning things, but I hope you can understand me what I am trying to do.
If this question is already answered please give me directions to that question and my appologise for that.. Many thanks in advance :)
Simply use WHERE clause with CAST:
SELECT SUM(Cast(Column1 as numeric(10,2))) as Total
FROM TABLE_Name
Where IsNumeric(Column1) = 1
Result:
TOTAL
4.7
Sample result in SQL Fiddle.
EDIT:
As #funkwurm pointed out, the column has a chance to have the value '.' (dot):
SELECT SUM(Cast(Column1 as numeric(10,2))) as Total
FROM TABLE_Name
Where IsNumeric(Column1 + 'e0') = 1
Fiddle.
You can "ignore" a row by adding 0.
SELECT
SUM(
CASE
WHEN ISNUMERIC(Column1)=1 THEN CONVERT(DECIMAL(10,5), Column1) --add the value to the sum
ELSE 0 --add 0 (i.e. ignore it)
END)
AS Total
FROM
TABLE_Name
try
select sum(case when isnumeric(cast(Column1 as decimal))
then cast(column1 as decimal) else 0 end) as total from table
assuming your field is varchar type
Why not using TRY_CONVERT:
SELECT SUM(TRY_CONVERT(float,Column1) AS Total
FROM TABLE_Name
Shortest query and easy to read! TRY_CONVERT returns NULL if conversion fails (e.g. if there are text values in Column1)
This Functions exists since SQL Server 2012 (i think).
#Jim Chen:
Isnumeric is a bit tricky a this point! In your solution there would be a problem if the value '-' or '+' is inside a Column:
SELECT ISNUMERIC('+') --returns TRUE!
SELECT ISNUMERIC('-') --returns TRUE!
==> SUM will FAIL!

Oracle NVL problem

I'm trying to pass a null value as something else in my db and it seems to work without a Where clause like so
select NVL(column1, '0') column1 from table1
produces this
0 test1
0 test2
1 test3
But when I add the where clause like so
select NVL(column1, '0') column1 from table1 where column1 <=1
it produces this
1 test3
But now if I add the following to the query it works
select NVL(column1, '0') column1
from table1
where NVL(column1, '0') <=1
But it seems like a long way round to get the value to show correctly with a Where clause
Any ideas what i'm doing wrong?
Thanks in advance
You cannot refer to an alias defined in the SELECT list from the WHERE clause. So if you apply the NVL function in the SELECT list, you'd need the same function call in the WHERE clause (as you've demonstrated) or you would need to apply the function in an inline view
SELECT column1
FROM (SELECT nvl(column1, 0) column1
FROM table1)
WHERE column1 <= 1
Note that for general sanity, if COLUMN1 is a NUMBER, the second parameter to NVL should also be a NUMBER. Otherwise, you're going to do implicit conversions and your comparison operations may end up using string comparison semantics rather than numeric comparison semantics. Since the string '12' is less than the string '2', that can lead to unexpected results.
you have shown the correct way.
an alternative would be to say
OR column IS NULL

Specify order of (T)SQL execution

I have seen similar questions asked elsewhere on this site, but more in the context of optimization.
I am having an issue with the order of execution of the conditions in a WHERE clause. I have a field which stores codes, most of which are numeric but some of which contain non-numeric characters. I need to do some operations on the numeric codes which will cause errors if attempted on non-numeric strings. I am trying to do something like
WHERE isnumeric(code) = 1
AND CAST(code AS integer) % 2 = 1
Is there any way to make sure that the isnumeric() executes first? If it doesn't, I get an error...
Thanks in advance!
The only place order of evaluation is guaranteed is CASE
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS integer) % 2
END = 1
Also just because it passes the isnumeric test doesn't guarantee that it will successfully cast to an integer.
SELECT ISNUMERIC('$') /*Returns 1*/
SELECT CAST('$' AS INTEGER) /*Fails*/
Depending upon your needs you may find these alternatives preferable.
Why not simply do it using LIKE?:
Where Code Not Like '%[^0-9]%'
Btw, either using my solution or using IsNumeric, there are some edge cases which might lead one to using a UDF such as 1,234,567 where IsNumeric will return 1 but Cast will throw an exception.
Why not use a CASE statement to say something like:
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS int) % 2 = 1
ELSE /* What ever else if not numeric */ END
You could do it in a case statement in the select clause, then limit by the value in an outer select
select * from (
select
case when isNum = 1 then CAST(code AS integer) % 2 else 0 end as castVal
from (
select
Case when isnumeric(code) = 1 then 1 else 0 end as isNum
from table) t
) t2
where castval = 1