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

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!

Related

SQL SERVER 2019 - LEN(FLOOR(CAST([value] AS FLOAT))) defaulting to 12

I am running the following code to get the length of a value before the decimal place:
SELECT LEN(FLOOR(CAST([VALUE] AS FLOAT))) FROM TABLE1 WHERE VALUE2 <> 'B'
The [VALUE] column in TABLE1 is of type nvarchar(30) hence the cast. The column also contains some non-numeric values but these are filtered out by the WHERE clause as they all have a 'B' value for VALUE2.
The code works as expected and returns '6' for values with 6 digits such as '123456.123'. It also works correctly for values with less than 6 digits. However, the code simply returns '12' for any value with greater than 6 digits such as '12345678'.
I've done some googling and can't seem to find a reason for this? Any explanations / alterations / alternatives would be much appreciated!
LENGTH() function expects string expression, so the float value is implicitly converted to string using scientific notation. The following statement demonstrates this issue and the unexpected result:
SELECT
LEN(FLOOR(CAST([VALUE] AS FLOAT))),
FLOOR(CAST([VALUE] AS FLOAT)),
CONVERT(varchar(50), FLOOR(CAST([VALUE] AS FLOAT)))
FROM (VALUES
(N'12345678')
) TABLE1 ([VALUE])
Result:
12 12345678 1.23457e+007
A possible solution, without using an integer (and/or float) conversion, is the following statement:
SELECT CHARINDEX(N'.', CONCAT([VALUE], N'.')) - 1
FROM (VALUES
(NULL),
(N'12345678'),
(N'123456.123'),
(N'99999.923')
) TABLE1 ([VALUE])
I am running the following code to get the length of a value before the decimal place:
This value is called the log base 10 plus 1 -- at least for numbers greater than 1. So how about using:
floor(log10(value)) + 1
You can tweak this for values less than 1 (including negative values) if that is needed.

SQL CONCAT drops zeros from expression

I am trying different ways to put a 0 in front of month less than 10.
I tried the following expression but the 0 get dropped.
What am I doing wrong?
CASE
WHEN month([Transact_Date]) < 10
THEN CONCAT(str(0),STR(month([Transact_Date]),1))
ELSE month([Transact_Date])
END AS month_w_0
Thanks!
Tom
I think a left padding trick is what you want here. Assuming your database be SQL Server:
SELECT RIGHT('00' + STR(MONTH([Transact_Date])), 2) AS month_w_0
FROM yourTable;
You don't need a CASE expression for this. In more recent versions of SQL Server, the FORMAT function might also be able to handle this.
I have tested this and it returns the result you want:
select CASE WHEN mnt < 10 THEN
concat('0' , mnt)
ELSE mnt
END AS month_w_0
from ( select month([Transact_Date]) mnt
from test_t) A
I have realised what your problem is. YOu can do it this way too:
SELECT
CASE
WHEN month([Transact_Date]) < 10
THEN CONCAT(str(0),STR(month([Transact_Date]),1))
ELSE STR(month([Transact_Date]),1)
END AS month_w_0
from test_t
The problem is only the else part and I believe that is because case when clause returns only one type od data. In your then part you have tryed to retunr string and in the else part number.
Hope this helps...
Here is a demo

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

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

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.

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))