SQL IsNumeric function - sql

SELECT IsNumeric('472369326D4')
is returning 1. Clearly, there is a aphabet D in the string. Why ?

472369326D4 is a valid float type. The D4 is translated as adding four 0 values, effectively multiplying the value before the D character by 10000.
Example Sql
SELECT cast('472369326D4' as float)
SELECT cast('472369326D3' as float)
SELECT cast('472369326D2' as float)
Output:
4723693260000
472369326000
47236932600

You probably want logic like this:
(case when str not like '%[^0-9]%' then 1 else 0 end) as isNumeric
Or, if you want to allow decimals and negative signs the logic is a little more cumbersome:
(case when str not like '%.%.%' and str not like '%[^.0-9]%' and
str not like '-%[^.0-9]%'
then 1 else 0
end)
I strongly recommend not using isnumeric(). It produces strange results. In addition to 'd', 'e' is allowed.
In SQL Server 2012+, you can try the following:
select x, isnumeric(x), try_convert(float, x)
from (values ('$'), ('$.'), ('-'), ('.')) v(x)
All of these return 1 for isnumeric, but NULL for the conversion, meaning that you cannot convert the value to a float. (You can run the code in SQL Server 2008 with convert() instead and watch the code fail.)

Related

CAST, SUM, CASE Issues

I am trying to accommodate for some rogue values in my database, that contain the string 'unknown', I want to set these to 0 and then sum the rest. But for some reason, this isnt happening. Here is what I have -
Values - VARCHAR(30) -
3
0.1
2
16
2
5
2
Unknown
2.4
7
Unknown
And here is my Cast,Sum,Case
Cast(sum(case when stake = 'Unknown' then 0 else stake end) as float) as totalStake
But I get this error - Conversion failed when converting the varchar value '0.1' to data type int.
Help!
Thanks
You must cast stake as a float:
sum(case when stake = 'Unknown' then 0.0 else cast(stake as float) end) as totalStake
You should explicitly convert to some sort of numeric values. Try this:
sum(try_convert(numeric(18, 4), stake)) as totalStake
Your code has at least two issues. First, your case expression returns an integer (because the first then has an integer). So, it tries to convert stake to an integer, which can generate an error.
Second, you should be doing arithmetic operations on data that is explicitly some sort of number type and not rely on implicit conversion.
You can try the following query using isnumeric() to check numeric data.
create table temp (stake VARCHAR(30))
insert into temp values
('3'), ('0.1'), ('2'), ('16'), ('2'), ('5'), ('2'), ('Unknown'), ('2.4'), ('7'), ('Unknown')
--Select * from temp
Select sum(Cast(stake as Float)) from temp where isnumeric(stake) = 1
To handle some exception like null values or . values only you can try this
Select SUM(TRY_CAST(stake as Float)) from temp
You can find the live demo Here.
Initial step would be to replace the 'Unknown' string with 0 using a replace function and then convert the column datatype to the one which allows to perform Aggregate functions and then perform SUM on top of that. The below query will work only for 'unknown' string, if you have different strings other than 'unknown' you might have to choose a different approach like using IsNumeric in Replace function and update the string value to 0.
select sum(cast((REPLACE(stake,'unknown',0)) as float)) from table
This happens because SQL has some problems while converting decimal values to integer values.
In facts, function sum returns integer values
I solved it using round function on the values1 variable ( sorry for using same name for table and column ):
select Cast(sum(case when values1 = 'Unknown' then 0 else round(values1, 2) end) as
float)as totalstrike
from values1

Datatype trouble in SQL Server 2008

I have a data something like this.
70.6
70.60
70.7
70.70
I can't use varchar as I need to perform arithmetic operations (>,< or floor), when I used float all records change to 70.7 and 70.6 from 70.70 and 70.60
When I changed to decimal(2,2), then all 70.6 records changed to 70.60.
Please suggest which data type suits for me.
Business need to retain 70.6 as it is and 70.60 as it is.
Currently data is in dataware house and stored in varchar.
I am preparing data mart and written query like
Result1 = CASE WHEN Dia = '' OR Dia > 28 OR M.Dia < 6 THEN 'Dia' END,
Result2 = CASE WHEN Width = '' OR Width > 16 OR M.Width < 3 THEN 'Width' END,
Result6 = CASE WHEN Bore] = '' OR FLOOR(LOG10(REVERSE(ABS(M.[Center Bore])+1)))+1 <> 2 THEN 'Bore' END,
From the mathematical point of view, there is no difference between 70.6 and 70.60. If your business rules must treat these values as different values, and you want to also be able to perform mathematical operations of them, you should keep them as decimal in your database, and I suggest adding a tinyint column that will specify the number of decimal digits of the original string value.
create a stored procedure that will get the value as string, calculate the number of decimal digits, convert the string to decimal, and save both of these values into the database.
When selecting the value you convert it to string and manipulate the decimal digits as you please.
You can use Convert(float, **) to convert the data from nvarchar.
I don't think there would be any difference can occur when your data is either 60.6 or 60.60 while performing arithmetic operation (>,< or floor)
select convert(float, '60.6') as val
Output: 60.6
as you mentioned in the comment
because I am checking data have only 1 digit after decimal
Then you can form get such a string using charindex and left.
select left(val, charindex('.', val) + 1) as new_val
from (select '60.6666' as val) as x
Input string : 60.6666
Output String : 60.6
now you can use
convert(float, new_val) as val
like,
select convert(float, left(val, charindex('.', val) + 1)) as val
from (select '60.6666' as val) as x

SQL query: convert

I'm trying to read a column from a database using a SQL query. The column consists of empty string or numbers as strings, such as
"7500" "4460" "" "2900" "2640" "1850" "" "2570" "9050" "8000" "9600"
I'm trying to find the right sql query to extract all the numbers (as integers) and removing the empty ones, but I'm stuck. So far I've got
SELECT *
FROM base
WHERE CONVERT(INT, code) IS NOT NULL
Done in program R (package sqldf)
If all columns are valid integers, you could use:
select * , cast(code as int) IntCode
from base
where code <> ''
To prevent cases when field code is not a valid number, use:
select *, cast(codeN as int) IntCode
from base
cross apply (select case when code <> '' and not code like '%[^0-9]%' then code else NULL end) N(codeN)
where codeN is not null
SQL Fiddle
UPDATE
To find rows where code is not a valid number, use
select * from base where code like '%[^0-9]%'
select *
from base
where col like '[1-9]%'
Example: http://sqlfiddle.com/#!6/f7626/2/0
If you don't need to test for the number being valid, ie. a string such as '909XY2' then this may run marginally faster, more or less depending on the size of the table
Is this what you want?
SELECT (case when code not like '%[^0-9]%' then cast(code as int) end)
FROM base
WHERE code <> '' and code not like '%[^0-9]%';
The conditions are repeated in the where and case on purpose. SQL Server does not guarantee that where filters are applied before logic in the select, so you can get an error with conversions. More recent versions of SQL Server have try_convert() to fix this problem.
Using sqldf with the default sqlite database and this test data:
DF <- data.frame(a = c("7500", "4460", "", "2900", "2640", "1850", "", "2570",
"9050", "8000", "9600"), stringsAsFactors = FALSE)
try this:
library(sqldf)
sqldf("select cast(a as aint) as aint from DF where length(a) > 0")
giving:
aint
1 7500
2 4460
3 2900
4 2640
5 1850
6 2570
7 9050
8 8000
9 9600
Note In plain R one could write:
transform(subset(DF, nchar(a) > 0), a = as.integer(a))

SQL Server: interpreting 'y' as BIT value

In C, when you compared true/false value to 1/0, it worked very well.
I would want the similar possibility with SQL Server - when I have a bit column, I would like to compare myBitField = 'y' / myBitField = 'n'
Is there anything I can do about that? Maybe change some SQL interpreter settings or something?
Example of what I would like to do:
select * from
(
select CAST(1 AS BIT) as result
) as main
where main.result = 'y'
Currently, it throws an error, and I would like it to return 1/true/'y', whatever, but I would like it to be able to make that comparison.
I suppose you want to do it for some yes/no thing. But this is generally a wrong concept, your application which is accessing the SQL Server should interpret y as a 1 and n as a 0 and afterwards set the correct parameters for the query. You should not (actually I'm temped to write "must not") do this in SQL Server, that's what you have a business logic for.
As others have said, BIT and CHAR / VARCHAR are entirely different datatypes. But if you want to cast them during the select, you can use CASE expression like so:
-- Reading string as BIT
SELECT CAST(CASE RESULT WHEN 'Y' THEN 1 WHEN 'N' THEN 0 ELSE NULL END AS BIT) RESULT
-- Reading BIT as string
SELECT CAST(CASE RESULT WHEN 1 THEN 'Y' WHEN 0 THEN 'N' ELSE NULL END AS CHAR(1)) RESULT
And that's about as far as your options go here, far as I can understand. :)

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