Counting occurrences of a value in multiple columns - postgres - sql

I have a table called fixtures (I have simplified for this example) and would like to populate the last two columns (*_plus_mc_per) with the percentage of times occurred for each number with a query run against the mc_* columns. It would look like this as an example
#mc = Match Corner # mc_per = Match Corner Percentage
| mc_0 | mc_1 | mc_3 | mc_4 | match_count | one_plus_mc_per | two_plus_mc_per |
| 1 | 4 | 3 | null | 3 | 100 | 66 |
At the point where I run my query it looks like
#mc = Match Corner # mc_per = Match Corner Percentage
| mc_0 | mc_1 | mc_3 | mc_4 | match_count | one_plus_mc_per | two_plus_mc_per |
| 1 | 4 | 3 | null | 3 | null | null |
So starting with the query for one_plus_mc_per I can do this
SELECT COUNT(*) FROM fixtures WHERE coalesce(mc_0,0) >= 1 AND id = 182;
# Using coalesce for dealing with null, will return a 0 if value null
This returns
| count |
| 1 |
If I run this query on each column individually the results returned would be
| count | count | count | count |
| 1 | 1 | 1 | 0 |
Thus enabling me to add all the column values up and divide by my match count. This makes sense (and I thank dmfay for getting me to think about his suggestion in a previous question)
My problem is I can't run this query 4 times for example as that is very ineffective. My SQL fu is not strong and was looking for a way to do this in one call to the database, enabling me to then take that percentage value and update the percentage column
Thanks

Try this:
SELECT
SUM(CASE WHEN coalesce(mc_0,0) >= 1 THEN 1 ELSE 0 END) count_0,
SUM(CASE WHEN coalesce(mc_1,0) >= 1 THEN 1 ELSE 0 END) count_1,
SUM(CASE WHEN coalesce(mc_3,0) >= 1 THEN 1 ELSE 0 END) count_3,
SUM(CASE WHEN coalesce(mc_4,0) >= 1 THEN 1 ELSE 0 END) count_4,
FROM
fixtures
WHERE id = 182;
It will return count of all the columns in single query
I am not sure though, whats the use of id = id in your query as it will always be true.
If you want count of columns *_mc for every row with > 0 condition, try this:
SELECT
(CASE WHEN coalesce(mc_0,0) >= 1 THEN 1 ELSE 0 END +
CASE WHEN coalesce(mc_1,0) >= 1 THEN 1 ELSE 0 END +
CASE WHEN coalesce(mc_3,0) >= 1 THEN 1 ELSE 0 END +
CASE WHEN coalesce(mc_4,0) >= 1 THEN 1 ELSE 0 END) as count
FROM
fixtures
WHERE id = 182;
UPDATE:
Calculating one_plus_mc_per
SELECT
CAST((CASE WHEN coalesce(mc_0,0) >= 1 THEN 1 ELSE 0 END +
CASE WHEN coalesce(mc_1,0) >= 1 THEN 1 ELSE 0 END +
CASE WHEN coalesce(mc_3,0) >= 1 THEN 1 ELSE 0 END +
CASE WHEN coalesce(mc_4,0) >= 1 THEN 1 ELSE 0 END)AS DECIMAL)/match_count as one_plus_mc_per
FROM
fixtures
WHERE id = 182;

Postgres has very nice capabilities for answering this type of question:
SELECT COUNT(*) FILTER (WHERE mc_0 >= 1) as count_0,
COUNT(*) FILTER (WHERE mc_1 >= 1) as count_1,
COUNT(*) FILTER (WHERE mc_3 >= 1) as count_3,
COUNT(*) FILTER (WHERE mc_4 >= 1) as count_4,
AVG ( (mc_0 >= 1)::int + (mc_1 >= 1)::int + (mc_3 >= 1)::int + (mc_4 >= 1)::int
) as one_plus_mc_per
FROM fixtures
WHERE id = 182;
The FILTER is ANSI-standard syntax. The conversion of booleans to numbers is a very convenient construct.

Related

How to do multiple actions in case when then in sql?

I want to do something like this:
select sum(case ttt.ind = 1 then 1 else 0 end) from ttt
I want to add a column to this query, called myresult which indicates if the value of ttt.istry is equal to 1.
Maybe like:
select
sum(case ttt.ind = 1 then 1, ttt.istry as myresult else 0 end)
from ttt
of course I got an error...
How would I do that?
My data is:
ttt.ind | ttt.istry
--------+----------
1 | 0
0 | 1
1 | 1
and so on...
Expected result:
ttt.ind | ttt.istry | myresult | sum
--------+-----------+----------+------
1 | 0 | 0 | 2
0 | 1 | null | 2
1 | 1 | 1 | 2
You don't say which database so I'll assume it's a modern one. You can use a window function and a CASE clause to do this.
For example:
select
ind,
istry,
case when ind = 1 then istry end as myresult,
sum(ind) over() as sum
from ttt
See live example at SQL Fiddle.
Your logic is a bit hard to follow, but your result set suggests:
select ind, istry,
(case when istry = 1 then 1
when sum(istry) over (partition by ind) = 1 then 0
end),
sum(ttt.ind) over () as sum_ind
from ttt;

How can I seperate one column into multiple columns depending on their value when selecting it?

I have a table called assignment_answers, which has the following attributes:
assignment_answers_id, question_id and order. The order is an attribute, which can take a value from 0 to 9.
I would like for every value that it can take to make it be displayed in a different column. For instance when the order has value 0, then I want it to be displayed in a column called number0. When it has value 1 I want it to be displayed in a column called number1.
Could someone help me with that? So far I have tried this but it does not work:
SELECT (CASE WHEN assessment_answers.order = 0
THEN(
select aq.order as number0
from assessment_answers)
END)
(CASE WHEN assessment_answers.order = 1
THEN(
select aq.order as number1
from assessment_answers)
END)
FROM assessment_answers
I get an error saying:
ERROR: syntax error at or near "("
LINE 6: (CASE WHEN assessment_questions."order" = 1
SAMPLE DATA
assignment_answers_id question_id order
1 1 0
2 1 0
3 2 1
desired output:
assignment_answers_id question_id order0 order1
1 1 0 null
2 1 0 null
3 2 null 1
You can try to use normal CASE WHEN
Query 1:
SELECT assignment_answers_id,
question_id,
(CASE WHEN order = 0 THEN order END) order0,
(CASE WHEN order = 1 THEN order END) order1
FROM assessment_answers
Results:
| assignment_answers_id | question_id | order0 | order1 |
|-----------------------|-------------|--------|--------|
| 1 | 1 | 0 | (null) |
| 2 | 1 | 0 | (null) |
| 3 | 2 | (null) | 1 |
Does this do what you want?
select (aa.order = 0)::int as order_0,
(aa.order = 1)::int as order_1,
(aa.order = 2)::int as order_2,
. . .
from assessment_answers aa;

SQL using more two columns with case

I can't find a good explanation for my problem.
I have a table:
user | 70Y | hospital
-------+-------+----------
1 | 18 | 1
2 | 70 | 1
3 | 90 | 0
I need to find is a how many people have more than 70Y, and if it has how many of those people are in the hospital.
I'm using this to find is his age more than 70:
SUM(CASE WHEN 70y > 70 THEN 1 ELSE 0 END) AS 'old_person'
but how do I find is he is in the hospital?
What I'm expecting from a table is:
| old_person | old_person_in_hospital|
+------------+-----------------------+
| 18 | 1 |
And if I would want to and more columns let's say check for 40Y old what is the easiest way to do so?
What I expect from table :
| old_person | 40y_person |
+-------------+---------------------+
| 18 | 16 |
in hospital | 1 | 2 |
You need a case for each column:
select
SUM(Case when [70y] > 70 then 1 else 0 end) old_person,
SUM(Case when [70y] > 70 and hospital = 1 then 1 else 0 end) old_person_in_hospital
from tablename
use another case for number in hospital count
select SUM(Case when 70y > 70 then 1 else 0 end) as old_person,
sum (Case when 70y > 70 and hospital=1 then 1 else 0 end ) hospital
from tbale
How about moving the condition to the where clause?
select count(*) as old_person,
sum(hospital) as old_person_in_hospital
from tablename
where [70y] > 70;
If you want to add more age groups, then you could use conditional aggregation. However, I might suggest that you use aggregation instead and put the results in different rows. For instance:
select (age / 10) as decade,
count(*) as num_people,
sum(hospital) as num_in_hospital
from tablename
group by (age / 10);

BigQuery : case when expression to Count from Same column but different conditions

I have a table with 2 columns as below:
Col 1 | col_stats
Field 1 | open
Field 2 | close
Field 1 | close
Field 1 | open
I want the ouput to be as :
Col1 | cnt_open | Cnt_close
Field 1 | 2 | 1
Field 2 | 0 | 1
**I wrote a query **
select col 1, count(case when col_stats= 'open' then 1 else 0 END) cnt_open,
count (case when col_stats= 'close' then 1 else 0 END ) cnt_close
from `project.dataset.tablename`
group by col1
Resultant output from above query is incorrect:
Col1 | cnt_open | Cnt_close
Field 1 | 2 | 2
Field 2 | 1 | 1
Can somebody let me know why the output is giving incorrect result for count even after case condition is applied?
Use countif():
select col1, countif(col_stat = 'open') as num_opens, countif(col_stat = 'closed') as num_closes
from t
group by col1;
In SQL count() counts the number of non-NULL values. Your code would work with sum(). But countif() is simpler and clearer.
Use null instead of 0:
select col1, count(case when col_stats= 'open' then 1 else null END) cnt_open,
count (case when col_stats= 'close' then 1 else null END ) cnt_close
from `project.dataset.tablename`
group by col1

SQL Server: select count of rows with not empty fields and total count of rows

Table has 4 int columns (Price0, Price1, Price2, Price3).
Example of table:
ID | Price0 | Price1 | Price2 | Price3 |
---+--------+--------+--------+--------+
1 | 10 | 20 | NULL | NULL |
2 | 70 | NULL | NULL | NULL |
3 | 30 | 40 | 50 | NULL |
How to query this table to get
total count of rows
and count of rows where count of filled Price columns >= N (for example N = 2)
Result must be:
Total | Filled
------+-------
3 | 2
This query show how many Price fileds is filled in each row
select
(select count(*) as filledFieldsCount
from (values (T.Price0), (T.Price1), (T.Price2), (T.Price3)) as v(col)
where v.col is not null
)
from Table1 T
Wouldn't with only 4 columns a simple nested case when be straightforward
select count(*),
sum(case when (
CASE WHEN Price1 is null THEN 0 ELSE 1 END +
CASE WHEN Price2 is null THEN 0 ELSE 1 END +
CASE WHEN Price3 is null THEN 0 ELSE 1 END +
CASE WHEN Price4 is null THEN 0 ELSE 1 END) >= 2 then 1 else 0 end)
FROM Table1
You can do this with conditional aggregation:
select count(*),
sum(case when tt.filledFieldsCount >= 2 then 1 else 0 end)
from Table1 T outer apply
(select count(*) as filledFieldsCount
from (values (T.Price0), (T.Price1), (T.Price2), (T.Price3)) as v(col)
where v.col is not null
) tt;
I moved the subquery to the from clause using apply. This is an example of a lateral join. In this case, it does the same thing as the subquery.