SQL Server Sum rows with string value - sql

I have a dynamic SQL query which returns rows like below with string values & numeric values.
EMP col1 col2 col3 col4 col5
----------------------------
A1 4 4 3 3 3
A2 4 2 5 3 3
A3 sd 3 3 1 sd
A4 3 4 3 3 3
Now I need a new column which sums col1 to col5 and creates a total sum column where it should ignore the string values as in row 3. There are no NULL values
How could I achieve this? Using ISNUMERIC might be the solution, but I'm not sure how to use it in such a scenario.

You can use a CASE Expression to determine whether the value is a number. If it is a number then either cast the value to an INT or DECIMAL data type, otherwise use 0 so it doesn't effect the sum.
SELECT
CASE WHEN ISNUMERIC(col1) = 1 THEN CAST(col1 as INT) ELSE 0 END
+ CASE WHEN ISNUMERIC(col2) = 1 THEN CAST(col2 as INT) ELSE 0 END
+ CASE WHEN ISNUMERIC(col3) = 1 THEN CAST(col3 as INT) ELSE 0 END
+ CASE WHEN ISNUMERIC(col4) = 1 THEN CAST(col4 as INT) ELSE 0 END
+ CASE WHEN ISNUMERIC(col5) = 1 THEN CAST(col5 as INT) ELSE 0 END as SumValue
FROM MyTable

If you're on SQL Server 2012, TRY_CONVERT avoids pitfalls commonly encountered with ISNUMERIC:
SELECT col1, col2, col3, col4, col5,
ISNULL(TRY_CONVERT(int, col1), 0) +
ISNULL(TRY_CONVERT(int, col2), 0) +
ISNULL(TRY_CONVERT(int, col3), 0) +
ISNULL(TRY_CONVERT(int, col4), 0) +
ISNULL(TRY_CONVERT(int, col5), 0) AS total
FROM Employee
SQLFiddle

You can do this with a big case statement:
select q.*,
((case when isnumeric(col1) = 1 then cast(col1 as int) else 0 end) +
(case when isnumeric(col2) = 1 then cast(col2 as int) else 0 end) +
(case when isnumeric(col3) = 1 then cast(col3 as int) else 0 end) +
(case when isnumeric(col4) = 1 then cast(col4 as int) else 0 end) +
(case when isnumeric(col5) = 1 then cast(col5 as int) else 0 end)
) as newcol
from q;
isnumeric() should be sufficient for your purposes. You might need fancier logic if you only want positive integers or want to exclude exponential notations or the like.

You're on the right track with isnumeric:
select
emp,
(case when isnumeric(col1) = 1 then cast(col1 as int) else 0 end) +
col2...
from table1

Related

SQL query to calculate a value from 3 different rows

col1 col2 cal_val
F 1879 1879
% 25 1409
$ -45 1454
First row basically outputs back the entry in column 2
2nd row would basically calculate 25% of the value in row1, column 3 and then subtract that value from previous value which is 1879-470 = 1409
I need to be able to calculate final value of 1454 (which is basically a subtraction from the 2nd row output value)1409 - (-45) which would equal to 1454
You can use conditional aggregation:
select ( (sum(case when col1 = 'F' then col2 end) *
sum(case when col1 = '%' then 1 - col2 / 100.0 end)
) -
sum(case when col1 = '$' then col2 end)
)
from t;
Here is a db<>fiddle.
create temporary table t_teble
(
col1 text ,
col2 numeric ,
cal_val numeric );
insert into t_teble
select 'F',1879, 1879 union all
select '%',25,1409 union all
select '$',-45,1454;
select ( (sum(case when col1 = 'F' then col2 end) *
sum(case when col1 = '%' then 1 - col2 / 100.0 end)
) -
sum(case when col1 = '$' then col2 end)
),
(sum(case when col1 = 'F' then col2 end) *
sum(case when col1 = '%' then 1 - col2 / 100.0 end)
)
from t_teble;

Return multiple rows from conditional group by without union

I am trying to build a query which supports conditional group by in SQLite DB.
Here is what I tried so far:
SELECT
case
when A>1 AND B>1 THEN 1
when X>1 AND Y>1 THEN 2
when C>1 AND D>1 THEN 3
END AS data_grp,
SUM(col1) AS col1,
SUM(col2) AS col2
FROM tbl
GROUP BY data_grp;
This Works pretty fine if only single case is true at a time. if multiple cases are true in a row then it returns the first case instead of all satisfying groups.
I have tried this by the union which works well but very slow. Is there any other way to fetch results fast with this conditional group.
Sample Data & Expected results:
DROP TABLE IF EXISTS tbl;
CREATE TABLE tbl
(
A INT,
B INT,
C INT,
D INT,
X INT,
Y INT,
col1 int,
col2 int
);
INSERT INTO tbl(A,B,C,D,X,Y,col1,col2) values (2,3,0,0,0,0,5,10);
INSERT INTO tbl(A,B,C,D,X,Y,col1,col2) values (0,0,0,0,8,10,3,2);
INSERT INTO tbl(A,B,C,D,X,Y,col1,col2) values (5,4,4,9,0,0,3,2);
SELECT
case
when A>1 AND B>1 THEN 1
when X>1 AND Y>1 THEN 2
when C>1 AND D>1 THEN 3
END AS data_grp,
SUM(col1) AS col1,
SUM(col2) AS col2
FROM tbl
GROUP BY data_grp;
Query Output :
"1" "8" "12"
"2" "3" "2"
Expected Output :
"1" "8" "12"
"2" "3" "2"
"3" "3" "2"
You can not use GROUP BY directly because of the overlapping groups.
You can use something like following, although this may also be slow.
WITH RECURSIVE
cnt(x) AS (
SELECT 1
UNION ALL
SELECT x+1 FROM cnt
LIMIT 3
)
SELECT x as data_grp, sum(col1), sum(col2)
FROM cnt,
(SELECT
case when A>1 AND B>1 THEN 1 ELSE 0 END as dg1,
case when X>1 AND Y>1 THEN 2 ELSE 0 END as dg2,
case when C>1 AND D>1 THEN 3 ELSE 0 END as dg3,
col1, col2
FROM tbl) t WHERE x=dg1 or x=dg2 or x=dg3
GROUP BY x
I am wary of summarizing data, where the result is on multiple rows and the totals don't match the original data. Of course, sometimes it is necessary, but here are two alternatives.
If you can be slightly flexible in your results, then you can concat the conditions together to get a more complex group:
SELECT ( (CASE WHEN A > 1 AND B > 1 THEN '1' ELSE '' END) ||
(CASE WHEN X > 1 AND Y > 1 THEN '2' ELSE '' END) ||
(CASE WHEN C > 1 AND D > 1 THEN '3' ELSE '' END)
) AS data_grp,
SUM(col1) AS col1, SUM(col2) AS col2
FROM tbl
GROUP BY data_grp;
I would actually write this as:
SELECT ( (CASE WHEN A > 1 AND B > 1 THEN '1' ELSE '0' END) ||
(CASE WHEN X > 1 AND Y > 1 THEN '1' ELSE '0' END) ||
(CASE WHEN C > 1 AND D > 1 THEN '1' ELSE '0' END)
) AS data_grp,
So data_grp gets a string of 0's and 1's indicating the group.
These results are not the same as your results. They are more what I would want, if I were looking at different groups -- I would want to see the overlaps between the groups.
Or, I would put the values in separate columns:
SELECT SUM(CASE WHEN A > 1 AND B > 1 THEN col1 ELSE 0 END) as sum1_1,
SUM(CASE WHEN X > 1 AND Y > 1 THEN col1 ELSE 0 END) as sum1_2,
SUM(CASE WHEN C > 1 AND D > 1 THEN col1 ELSE 0 END) as sum1_3,
SUM(CASE WHEN A > 1 AND B > 1 THEN col2 ELSE 0 END) as sum2_1,
SUM(CASE WHEN X > 1 AND Y > 1 THEN col2 ELSE 0 END) as sum2_2,
SUM(CASE WHEN C > 1 AND D > 1 THEN col2 ELSE 0 END) as sum2_3
FROM tbl;
These are the same results, but pivoted differently.

Two select statement with case

I have two queries and I have a value if value is 1 then first query will execute if value is 2 then second. How can i achieve this, my query is
Query one if value is 1
SELECT count (CASE WHEN col1 = 9 THEN 1 ELSE 0 END )AS "matches" ,
CAST( CASE WHEN col1 = 9 THEN 1 ELSE 0 END AS VARCHAR(10)) + ' / 1' AS "match by"
FrOM table a
where ( SELECT CASE WHEN col1 = 9 THEN 1 ELSE 0 END AS "matches" ) >= 1
group by ( CASE WHEN col1 = 9 THEN 1 ELSE 0 END )
)
Query 2 if value 2
SELECT count (CASE WHEN col1 = 9 THEN 1 ELSE 0 END +
CASE WHEN col2 = 10 THEN 1 ELSE 0 END
)AS "matches" ,
CAST( CASE WHEN col1 = 9 THEN 1 ELSE 0 END
+ CASE WHEN col2 = 10 THEN 1 ELSE 0 END AS VARCHAR(10)) + '/ 2' AS "match by"
FrOM table a
where ( SELECT CASE WHEN col1 = 9 THEN 1 ELSE 0 END +
CASE WHEN col2 = 10 THEN 1 ELSE 0 END AS "NUM_OF_MATCHES" ) >= 1
group by ( CASE WHEN col1 = 9 THEN 1 ELSE 0 END+
CASE WHEN col2 = 10 THEN 1 ELSE 0 END )
I have try it using case but it is not working
I want if value = 1 then first query will run and if value = 2 then second
although both queries doing same work but if value is 1 then col1 logic will take only col1 and if value is 2 then it will take col1 and col2 if value is 3 then it will take col1 col2 and col3 till 6 col
You can make use of a simple if else if and execute both the queries.
IF(#value=1)
BEGIN
SELECT count (CASE WHEN col1 = 9 THEN 1 ELSE 0 END )AS "matches" ,
CAST( CASE WHEN col1 = 9 THEN 1 ELSE 0 END AS VARCHAR(10)) + ' / 1' AS "match by"
FrOM table a
where ( SELECT CASE WHEN col1 = 9 THEN 1 ELSE 0 END AS "matches" ) >= 1
group by ( CASE WHEN col1 = 9 THEN 1 ELSE 0 END )
)
END
ELSE IF(#value=2)
BEGIN
SELECT count (CASE WHEN col1 = 9 THEN 1 ELSE 0 END +
CASE WHEN col2 = 10 THEN 1 ELSE 0 END
)AS "matches" ,
CAST( CASE WHEN col1 = 9 THEN 1 ELSE 0 END
+ CASE WHEN col2 = 10 THEN 1 ELSE 0 END AS VARCHAR(10)) + '/ 2' AS "match by"
FrOM table a
where ( SELECT CASE WHEN col1 = 9 THEN 1 ELSE 0 END +
CASE WHEN col2 = 10 THEN 1 ELSE 0 END AS "NUM_OF_MATCHES" ) >= 1
group by ( CASE WHEN col1 = 9 THEN 1 ELSE 0 END+
CASE WHEN col2 = 10 THEN 1 ELSE 0 END )
END
still are you using CASE Statement, why dont you try to use IIF Statement, IIF Statement is more efficient and fast compare to CASE Statement.
e.g.
select IIF(1=1, (your desired column1),(your desired column2)) as matches
you can also use nested IIF Statement in single query like CASE Statement..
select IIF(1=1, (IIF(2=2, (your desired column1),(your desired column2))),(your desired column3)) as matches.

How can I SUM rows that have at least X Boolean 1s?

Example table:
col1 col2 col3 col4
Abe 1 0 1
Bob 0 1 0
Cam 1 1 1
Dan 1 0 0
Eve 1 1 1
How can I query so that I can SUM how many people in col1 have a total of 2 1s? (So, since Abe is the only person with 2 1s, the query outputs the number 1.)
Thanks.
Select count(*) from table_name where col2+col3+col4=2
If Your columns value contains only 0 and 1 then Vlad Călin Buzea and scaisEdge answer will work for you. But if it can have other than 0 and 1, You need this.
SELECT COUNT(*)
FROM Table_Name
WHERE (CASE WHEN col2 = 1 THEN 1 ELSE 0 END) +
(CASE WHEN col3 = 1 THEN 1 ELSE 0 END) +
(CASE WHEN col4 = 1 THEN 1 ELSE 0 END) = 2
;WITH cte AS (
SELECT 'Abe' AS Col1, CAST(1 AS BIT) as Col2, CAST(0 AS BIT) as Col3, CAST(1 AS BIT) AS Col4
UNION ALL
SELECT 'Bob' AS Col1, CAST(0 AS BIT) as Col2, CAST(1 AS BIT) as Col3, CAST(0 AS BIT) AS Col4
UNION ALL
SELECT 'Cam' AS Col1, CAST(1 AS BIT) as Col2, CAST(1 AS BIT) as Col3, CAST(1 AS BIT) AS Col4
UNION ALL
SELECT 'AbeWNull' AS Col1, CAST(1 AS BIT) as Col2, CAST(NULL AS BIT) as Col3, CAST(1 AS BIT) AS Col4
)
SELECT *
FROM
cte
WHERE
ISNULL(CAST(Col2 AS INT),0) + ISNULL(Col3,0) + ISNULL(Col4,0) = 2
The cte gives you some test data. 2 things. 1 if it is a Boolean or bit column you could have to cast/convert to an integer or numeric datatype at least one of the columns or the addition wont work. And yes I tested and needed to. Second, if you don't deal with the potential of a null you may not get the answer you want.
For example.
SELECT 1 + NULL + 1
Will give you NULL
Using SUM on a column isn't the same and it will just ignore the nulls, but not during the addition line.
Use addition of columns in where clause and select count()
select count(*)
from my_table
where CAST(ifnull(col2,false) AS SIGNED INTEGER) +
CAST(ifnull(col3,false) AS SIGNED INTEGER) +
CAST(ifnull(col4,false) AS SIGNED INTEGER) = 2;

Concatenate SQL table rows with conditions in single text

I have a SQL table like this
col1 col2 col3
1 0 1
1 1 1
0 1 1
1 0 0
0 0 0
I am expecting output as like this
col1 col2 col3 NewCol
1 0 1 SL,PL
1 1 1 SL,EL,PL
0 1 1 EL,PL
1 0 0 SL
0 0 0 NULL
The condition for this is if col1>0 then SL else ' ', if col2>0 EL else ' ', if col3>0 PL else ' '
I tried to use Concatenate many rows into a single text string? but didn't able to achieve the desired result properly
I have tried It is working fine with a message
Invalid length parameter passed to the LEFT or SUBSTRING function.
WITH CTE AS (
SELECT col1, col2, col3,
CASE WHEN col1 > 0 THEN 'SL,' ELSE '' END +
CASE WHEN col2 > 0 THEN 'EL,' ELSE '' END +
CASE WHEN col3 > 0 THEN 'PL,' ELSE '' END AS NewCol
FROM Employee
)
SELECT col1, col2, col3,
substring(NewCol, 1, len(NewCol) - 1) AS NewCol
FROM CTE
But again my last condition is not matching if all columns is 0 then I have to show NULL as per desired output.
Find the attach fiddle http://sqlfiddle.com/#!6/2bd6a/1
The issue with your code example is that when all columns are 0 then the length is 0 and the substring function will throw an error.
Use nullif to fix it: substring(NewCol, 1, len(nullif(NewCol,'')) - 1) AS NewCol
You could also change to appending the delimiter on the front and use STUFF.
STUFF('',1,1,'') will return NULL rather than an error.
WITH
Employee(col1, col2, col3) AS (
SELECT 1,1,1 UNION ALL
SELECT 0,0,0
),
CTE AS (
SELECT col1, col2, col3,
CASE WHEN col1 > 0 THEN ',SL' ELSE '' END +
CASE WHEN col2 > 0 THEN ',EL' ELSE '' END +
CASE WHEN col3 > 0 THEN ',PL' ELSE '' END AS NewCol
FROM Employee
)
SELECT col1,
col2,
col3,
STUFF(NewCol, 1, 1, '')
FROM CTE
Returns
+------+------+------+------------------+
| col1 | col2 | col3 | (No column name) |
+------+------+------+------------------+
| 1 | 1 | 1 | SL,EL,PL |
| 0 | 0 | 0 | NULL |
+------+------+------+------------------+
You have to check with NULLIF to do this trick
Two ways
SELECT col1, col2, col3,
nullif(CASE WHEN col1 = 1 THEN 'SL,' ELSE '' END +
CASE WHEN col2 = 1 THEN 'EL,' ELSE '' END +
CASE WHEN col3 = 1 THEN 'PL,' ELSE '' END,'') AS NewCol
FROM Employee
OR
SELECT
col1,
col2,
col3,
substring(nullif(NewCol,''), 1, len(NewCol) - 1) AS NewCol
FROM
CTE