Querying Counts on Field Values in a Single Table - sql

I have a single product table with multiple fields which contain user evaluations of various attributes:
product | attr_1_eval | attr_2_eval | attr_3_eval
ABC | Correct | Incorrect | Null
DEF | Incorrect | Null | Null
XYZ | Undetermined | Null | Incorrect
123 | Null | Undetermined | Correct
456 | Incorrect | Correct | Correct
I need to write a query which totals up those attribute evaluations across all products (where not null):
evaluation | correct | incorrect | undetermined
attr_1 | 1 | 2 | 1
attr_2 | 1 | 1 | 1
attr_3 | 2 | 1 | 0
This SQL gets me part way there:
SELECT
SUM(CASE WHEN attr_1_eval = 'Correct' then 1 else 0 END) AS correct,
SUM(CASE WHEN attr_1_eval = 'Incorrect' then 1 else 0 END) AS incorrect,
SUM(CASE WHEN attr_1_eval = 'Undetermined' then 1 else 0 END) AS undetermined,
SUM(CASE WHEN attr_2_eval = 'Correct' then 1 else 0 END) AS correct,
...
FROM product
But it doesn't group attr_1, attr_2.. by rows with error counts in the columns (as in the desired result set above). I'm using Postgres but help in any flavor of SQL would be most welcome.

Could you do 3 unions?
SELECT
'attr_1' AS evaluation,
SUM(CASE WHEN attr_1_eval = 'Correct' then 1 else 0 END) AS correct,
SUM(CASE WHEN attr_1_eval = 'Incorrect' then 1 else 0 END) AS incorrect,
SUM(CASE WHEN attr_1_eval = 'Undetermined' then 1 else 0 END) AS undetermined
FROM product
UNION
SELECT
'attr_2' AS evaluation,
SUM(CASE WHEN attr_2_eval = 'Correct' then 1 else 0 END) AS correct,
SUM(CASE WHEN attr_2_eval = 'Incorrect' then 1 else 0 END) AS incorrect,
SUM(CASE WHEN attr_2_eval = 'Undetermined' then 1 else 0 END) AS undetermined
FROM product
UNION
SELECT
'attr_3' AS evaluation,
SUM(CASE WHEN attr_3_eval = 'Correct' then 1 else 0 END) AS correct,
SUM(CASE WHEN attr_3_eval = 'Incorrect' then 1 else 0 END) AS incorrect,
SUM(CASE WHEN attr_3_eval = 'Undetermined' then 1 else 0 END) AS undetermined
FROM product
It's not the most elegant/efficient solution probably but it should get what you want

It's a little brute force, and I hate the fact that it scans the table three times, but this does appear to get the desired output. I'm sorry I don't know PostGres, but this should work in Oracle:
select
Attribute_name,
Sum (correct) as Correct,
sum (incorrect) as Incorrect,
sum (undetermined) as Undetermined
from
(
select
'attr_1' as Attribute_Name,
decode (attr_1_eval, 'Correct', 1, 0) as correct,
decode (attr_1_eval, 'Incorrect', 1, 0) as incorrect,
decode (attr_1_eval, 'Undetermined', 1, 0) as undetermined
from product
union all
select
'attr_2',
decode (attr_2_eval, 'Correct', 1, 0),
decode (attr_2_eval, 'Incorrect', 1, 0),
decode (attr_2_eval, 'Undetermined', 1, 0)
from product
union all
select
'attr_3',
decode (attr_3_eval, 'Correct', 1, 0),
decode (attr_3_eval, 'Incorrect', 1, 0),
decode (attr_3_eval, 'Undetermined', 1, 0)
from product
)
group by Attribute_Name

Related

USE ELSE 0 doesn't work as expected in SQL

I have the following SQL query:
SELECT
modal_text,
COUNT(CASE WHEN ab_group = "control" THEN 1 ELSE 0 END)
FROM
onboarding_modals
GROUP BY
1
ORDER BY
1;
This doesn't work as expected (it will count more than expected), but when I remove the ELSE 0 in aggregate function, it works as expected:
SELECT
modal_text, COUNT(CASE WHEN ab_group = "control" THEN 1 END)
FROM
onboarding_modals
GROUP BY
1
ORDER BY
1;
Could someone explain me why having the ELSE 0 will make it count more data than it should be?
*It will also work if I use ELSE NULL
Because a COUNT(SomeColumn) doesn't count the NULL's in a column.
COUNT(1) or COUNT(*) count the rows.
And so does a COUNT(CASE WHEN x=1 THEN 1 ELSE 0 END)
This has no NULL's to ignore, because it's either 1 or 0.
But a CASE WHEN x=1 THEN 1 END
is just the implicit shorter syntax for
CASE WHEN x=1 THEN 1 ELSE NULL END
So it's normal to COUNT without the ELSE.
COUNT(DISTINCT CASE WHEN x=1 THEN t.ID END)
If you do want to use an ELSE, then do it with a SUM
SUM(CASE WHEN x=1 THEN 1 ELSE 0 END)
Use SUM() instead of COUNT(), as in:
SELECT
modal_text,
SUM(CASE WHEN ab_group = "control" THEN 1 ELSE 0 END)
FROM
onboarding_modals
GROUP BY
1
ORDER BY
1;
Could someone explain me why having the ELSE 0 will make it count more data than it should be?
Becasue COUNT(CASE WHEN ab_group = "control" THEN 1 ELSE 0 END) is different to COUNT(CASE WHEN ab_group = "control" THEN 1 END) let's see a sample below
we can see there will be count when we use count(1) or count(0) except count(null) count function will not be count when the value is null
Query 1:
SELECT COUNT(1)
| COUNT(1) |
|----------|
| 1 |
SELECT COUNT(0)
| COUNT(0) |
|----------|
| 1 |
SELECT COUNT(NULL)
| COUNT(NULL) |
|-------------|
| 0 |
Query 2:
SELECT SUM(1)
| SUM(1) |
|--------|
| 1 |
SELECT SUM(0)
| SUM(0) |
|--------|
| 0 |
SELECT SUM(NULL)
| SUM(NULL) |
|-----------|
| (null) |
Results:

Select sum based on value of other column

I have a table with values like
ID | CODE | QUANTITY
====================
1 | 2 | 20
2 | 2 | 40
3 | 5 | 10
4 | 6 | 15
5 | 5 | 20
6 | 6 | 50
7 | 6 | 10
8 | 7 | 20
9 | 8 | 100
I have a requirement to get the sum of all quantities with "CODE" = 2. However, if the sum is 0
then return the sum of all quantities where "CODE" in (5,6). The idea is to ignore all other codes except 2, 5, and 6, with 2 as the first preference for sum.
I have tried this
WITH CTE AS(
SELECT
SUM(CASE WHEN CODE = '2' THEN QUANTITY ELSE 0 END) AS QUANTITY1,
SUM(CASE WHEN CODE IN ('5', '6') THEN QUANTITY ELSE 0 END) AS QUANTITY2
FROM TABLE1
)
SELECT CASE
WHEN QUANTITY1 <> 0 THEN QUANTITY1
ELSE QUANTITY2
END
FROM CTE
It does work but I feel it can be improved and can be done in minimum steps. How can I improve it?
Edit1: The value of QUANTITY column can be 0 in TABLE1
Edit2: sqlfiddle
For the sum of quantities with CODE = '2' use ELSE 0 in the CASE expression and NULLIF(), so that the result is NULL even if the sum is 0:
SELECT COALESCE(
NULLIF(SUM(CASE WHEN CODE = '2' THEN QUANTITY ELSE 0 END), 0),
SUM(CASE WHEN CODE IN ('5', '6') THEN QUANTITY END)
)
FROM TABLE1
You can use ELSE for quantities with CODE IN ('5', '6') too:
SELECT COALESCE(
NULLIF(SUM(CASE WHEN CODE = '2' THEN QUANTITY ELSE 0 END), 0),
SUM(CASE WHEN CODE IN ('5', '6') THEN QUANTITY ELSE 0 END)
)
FROM TABLE1
See the demo.
If quantity is always greater than 0 in the underlying table, you can use COALESCE():
SELECT COALESCE(SUM(CASE WHEN CODE = '2' THEN QUANTITY END) AS QUANTITY1,
SUM(CASE WHEN CODE IN ('5', '6') THEN QUANTITY END),
0) AS QUANTITY2
FROM TABLE1

SQL sum multiple fields with conditions

I'm gonna to make a SUM function with these columns of Profile table when each column is greater than 1.
How can I do it?
bank docs personal
2 1 2
The following counts the rows where the values are greater than 1:
select sum(case when bank > 1 then 1 else 0 end) as bank,
sum(case when docs > 1 then 1 else 0 end) as docs,
sum(case when personal > 1 then 1 else 0 end) as personal
from Profile;
The following counts the number of columns within a row with values greater than 1:
select p.*,
(case when bank > 1 then 1 else 0 end +
case when docs > 1 then 1 else 0 end +
case when personal > 1 then 1 else 0 end
) as cnt
from Profile p;
These are the two most sensible interpretations of our question.
The sum of rows where each column value is greater than one:
select sum(bank + docs + personal)
from Profile
where bank > 1 and docs > 1 and personal > 1
;
with t as (
select 0 as v0, 1 as v1 from dual
union all select 3 as v0, 2 as v1 from dual
)
select sum(v0 + v1)
from t
where v0 > 1 and v1 > 1
;
| SUM(V0+V1) |
| ---------: |
| 5 |
db<>fiddle here
The sum of each individual column when the column is greater than 1: (untested)
select sum(case when isnull(bank,0) > 1 then bank else 0 end) banktotal
,sum(case when isnull(docs,0) > 1 then docs else 0 end) docstotal
,sum(case when isnull(personal,0) > 1 then personal else 0 end) personaltotal
from Profile
isnull() may or may not be needed based on your data and table design.

SQL separate the count of one column

I have a SQL table that contains three columns:
userId
userName
item
and I created this SQL query which will count all the items types of one user:
select
count(ItemID) as 'count of all items types',
userId,
userName
from
userTable
where
ItemID in (2, 3, 4)
and userId = 1
group by
userId, userName
The result will be like this:
+--------+----------+--------------------------+
| userId | userName | count of all items types |
+--------+----------+--------------------------+
| 1 | kim | 25 |
and I am looking for a way to separate the counting of itemes types, so the result should be like this:
+--------+----------+----------------+----------------+-----------------+
| userId | userName | count of item1 | count of item2 | count of item3 |
+--------+----------+----------------+----------------+-----------------+
| 1 | kim | 10 | 10 | 5 |
SELECT
userID,
userName,
SUM(CASE WHEN ItemID = 2 THEN 1 ELSE 0 END) AS count_of_item1,
SUM(CASE WHEN ItemID = 3 THEN 1 ELSE 0 END) AS count_of_item2,
SUM(CASE WHEN ItemID = 4 THEN 1 ELSE 0 END) AS count_of_item3
FROM
My_Table
GROUP BY
userID,
userName
This is called conditional aggregation. Use CASE for this.
With COUNT:
select
count(case when ItemID = 1 then 1 end) as count_item1,
count(case when ItemID = 2 then 1 end) as count_item2,
count(case when ItemID = 3 then 1 end) as count_item3
...
(then 1 could also be anything else except null, e.g. then 'count me'. This works because COUNT counts non-null values and when omitting the ELSE in CASE WHEN you get null. You could also explicitly add else null.)
Or with SUM:
select
sum(case when ItemID = 1 then 1 else 0 end) as count_item1,
sum(case when ItemID = 2 then 1 else 0 end) as count_item2,
sum(case when ItemID = 3 then 1 else 0 end) as count_item3
...
This is how you would do it :
select userId,
username,
SUM(CASE WHEN ItemID = '2' THEN 1 ELSE 0 END) AS Item2-Cnt,
SUM(CASE WHEN ItemID = '3' THEN 1 ELSE 0 END) AS Item3-Cnt,
SUM(CASE WHEN ItemID = '4' THEN 1 ELSE 0 END) AS Item4-Cnt
FROM userTable
GROUP BY userID, userName

Case With Having

I have Plant, Material, Movement type and Date.
My requirement is I want the MAX date in Material and Plant combination, with Movement type condition
First I need MAX date in Material and Plant combination, where Movement type is 602 , if 602 is null then 601, if 601 is null then 102, if 102 is null then 101 if 101 also is null then other Movement types.
For this I used below code in derived table in IDT but I am not able to get the correct results:
SELECT
DISTINCT
Table__1."0PLANT",
Table__1."0MATERIAL",
Max(Table__1."0PSTNG_DATE") as Last_Sales_Date
FROM
"NM1"."PUBLIC"."IZDSO_DMR" Table__1
Where
CASE
WHEN (Table__1."0MOVETYPE"=602) THEN (Table__1."0MOVETYPE"=602)
WHEN (Table__1."0MOVETYPE"=602) IS NULL THEN (Table__1."0MOVETYPE"=601)
ELSE
(Table__1."0MOVETYPE"=601) END
Group By
Table__1."0MATERIAL",
Table__1."0PLANT"
I tried only for 602, 601 Movement types only
Please help me to get the correct result
This is your where clause:
Where (CASE WHEN (Table__1."0MOVETYPE"=602) THEN (Table__1."0MOVETYPE"=602)
WHEN (Table__1."0MOVETYPE"=602) IS NULL THEN (Table__1."0MOVETYPE"=601)
ELSE (Table__1."0MOVETYPE"=601)
END)
This will actually work with some SQL engines, because a comparison is turned into a number. However, I don't think this is what you want. You are looking to prioritize the max() value, based on the 0MoveType column.
SELECT "0PLANT", "0MATERIAL",
(case when sum(case when "0MOVETYPE" = 602 then 1 else 0 end) > 0
then max(case when "0MOVETYPE" = 602 then "0PSTNG_DATE" end)
when sum(case when "0MOVETYPE" = 601 then 1 else 0 end) > 0
then max(case when "0MOVETYPE" = 601 then "0PSTNG_DATE" end)
when sum(case when "0MOVETYPE" = 102 then 1 else 0 end) > 0
then max(case when "0MOVETYPE" = 102 then "0PSTNG_DATE" end)
when sum(case when "0MOVETYPE" = 101 then 1 else 0 end) > 0
then max(case when "0MOVETYPE" = 101 then "0PSTNG_DATE" end)
end) as Last_Sales_Date
\FROM "NM1"."PUBLIC"."IZDSO_DMR" Table__1
Group By "0MATERIAL", "0PLANT";
This returns the last sales date for "602" if any are present. If none are present, then it returns the last sales date of "601". If neither "601" nor "602" are present, then it goes to "102" and so on.