Sum and count of records - sql

I have a table with some records:
Col1
Col2
Col3
Col4
1
Row1
99
152
1
Row2
99
20
5
Row3
98
34
1
Row4
120
18
7
Row5
27
74
I need to get the sum of Col4 values where Col1 = 1, sum of Col4 values where Col3 = 99 and Col1 = 1, sum of Col4 values where Col3 <> 99 and Col1 = 1, total count of records where Col1 = 1, count of records where Col3 = 99 and Col1 = 1, count of records where Col3 <> 99 and Col1 = 1 (there is a possibility that there will be no records that meet the criteria).
My SQLite statement looks like that:
query.SQL.Text:= 'SELECT IFNULL(sum(Col4), 0), '+
'IFNULL(sum(case when Col3 = 99 then Col4 else 0 end), 0), ' +
'IFNULL(sum(case when Col3 <> 99 then Col4 else 0 end), 0), ' +
'IFNULL(count(*), 0), ' +
'IFNULL(sum(case Col3 = 99 then 1 else 0 end), 0), ' +
'IFNULL(sum(case Col3 <> 99 then 1 else 0 end), 0) ' +
'FROM myTable WHERE Col1 = :_Col1';
Is there a way to simplify it?

SQLite evaluates boolean expressions to 1 (true) or 0 (false), so a CASE expression like:
case when Col3 = 99 then 1 else 0 end
can be simplified to:
Col3 = 99
Also, the ELSE part in a CASE expression like:
CASE WHEN Col3 = 99 THEN Col4 ELSE 0 END
is not needed because later you use IFNULL() to return 0 in case it returns NULL.
Finally, COUNT(*) never returns NULL, so IFNULL() is not needed in this case.
Simplify your code to this:
SELECT IFNULL(SUM(Col4), 0),
IFNULL(SUM(CASE WHEN Col3 = 99 THEN Col4 END), 0),
IFNULL(SUM(CASE WHEN Col3 <> 99 THEN Col4 END), 0),
COUNT(*),
IFNULL(SUM(Col3 = 99), 0),
IFNULL(SUM(Col3 <> 99), 0)
FROM myTable
WHERE Col1 = 1;
See the demo.

Related

Count occurance in a row in SQLite just by SQL?

I have a DB contain 5 columns, all are integers range from 1~5 e.g.
1,1,2,3,1
5,1,2,3,4
4,2,3,2,1
....
is there a way to count number of occurrence of, say 1 in first row by just SQL ? (in this case, 3 should be return). select count() just deal with number of records return in column manner. Thanks.
Regds
LAM Chi-fung
You can combine case with count to get five columns, each with the count of the values 1 - 5:
select
case when col1 = 1 then 1 else 0 end + 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 + case when col5 = 1 then 1 else 0 end as count_1
,case when col1 = 2 then 1 else 0 end + case when col2 = 2 then 1 else 0 end + case when col3 = 2 then 1 else 0 end + case when col4 = 2 then 1 else 0 end + case when col5 = 2 then 1 else 0 end as count_2
,case when col1 = 3 then 1 else 0 end + case when col2 = 3 then 1 else 0 end + case when col3 = 3 then 1 else 0 end + case when col4 = 3 then 1 else 0 end + case when col5 = 3 then 1 else 0 end as count_3
,case when col1 = 4 then 1 else 0 end + case when col2 = 4 then 1 else 0 end + case when col3 = 4 then 1 else 0 end + case when col4 = 4 then 1 else 0 end + case when col5 = 4 then 1 else 0 end as count_4
,case when col1 = 5 then 1 else 0 end + case when col2 = 5 then 1 else 0 end + case when col3 = 5 then 1 else 0 end + case when col4 = 5 then 1 else 0 end + case when col5 = 5 then 1 else 0 end as count_5
from table_x
You can use:
select t.*,
(col1 = 1) + (col2 = 1) + (col3 = 1) + (col4 = 1) + (col5 = 1) as num_1s
from t
SQLite treats boolean expressions like numbers in an arithmetic context, with 1 for true and 0 for false. You can also express this more verbosely as:
select t.*,
(case when col1 = 1 then 1 else 0 end +
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 +
case when col5 = 1 then 1 else 0 end +
) as num_1s
from t;
By unpivoting the data, you can get the count for all values in all columns in a single query, without the need to know the possible values in advance
sqlite> select * from t;
col1 col2 col3 col4 col5
---------- ---------- ---------- ---------- ----------
1 1 2 3 1
5 1 2 3 4
4 2 3 2 1
sqlite> select col
...> ,count(*) as cnt
...> from ( select col1 as col from t
...> union all select col2 from t
...> union all select col3 from t
...> union all select col4 from t
...> union all select col5 from t
...> )
...> group by col
...> ;
col cnt
---------- ----------
1 5
2 4
3 3
4 2
5 1
sqlite>

Update row in a table based on a column in the same table

How can I update rows in a table based on values in a column in the same table?
TableA:
col1 col2 col3 total col_num
NULL NULL NULL 100 1
NULL NULL NULL 200 2
NULL NULL NULL 300 3
Result after update:
TableA:
col1 col2 col3 total col_num
100 NULL NULL 100 1
NULL 200 NULL 200 2
NULL NULL 300 300 3
Unless you resort to somehow dynamically constructing the SQL statement, you can't do this generically. However, for a closed set of columns, you could use a bunch of case expressions:
UPDATE tableA
SET col1 = CASE col_num WHEN 1 THEN total ELSE col1 END,
col2 = CASE col_num WHEN 2 THEN total ELSE col2 END,
col3 = CASE col_num WHEN 3 THEN total ELSE col3 END
You can use case expressions:
update tablea
set
col1 = case when col_num = 1 then total end,
col2 = case when col_num = 2 then total end,
col3 = case when col_num = 3 then total end
You would possibly include some logic to update only non-null columns
update tablea
set
col1 = case when col1 is null and col_num = 1 then total end,
col2 = case when col2 is null and col_num = 2 then total end,
col3 = case when col3 is null and col_num = 3 then total end
where
(col1 is null and col_num = 1)
or (col2 is null and col_num = 2)
or (col3 is null and col_num = 3)

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

How to deal with duplicate rows in SQL?

The table has duplicate IDs from a large table. I want to get one output for each ID. What's the best way to do it?
MyTable
ID Col1 Col2
1 X A
1 Y B
1 Z C
2 X D
2 Y E
3 Z F
3 W G
If Col1 = 'X' and Col2 ='A', then 'Y' is the output for ID1
If Col1 = 'X' and Col2 !='A', then 'N' is the output for ID2
If Col1 != 'X', then 'Y' is the output for ID3
If Col1 = 'X' and Col2 ='A', then 'Y' is the output for ID1
If Col1 = 'X' and Col2 !='A', then 'N' is the output for ID2
If Col1 != 'X', then 'Y' is the output for ID3
I assume the conditions above need to be true for only 1 row per id. You can use conditional aggregation to check whether the condition applies to at least 1 row per group:
select id,
case when count(case when Col1 = 'X' and Col2 = 'A' then 1 end) > 0 then 'Y'
when count(case when Col1 = 'X' and Col2 <> 'A' then 1 end) > 0 then 'N'
when count(case when Col1 = 'X' then 1 end) > 0 then 'Y'
else '?'
end as output
from mytable
group by id

SQL Server Sum rows with string value

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