简体   繁体   中英

Calculating Percentages in Postgres

I'm completely new to PostgreSQL. I have the following table called my_table:

a    b    c        date
1    0    good     2019-05-02
0    1    good     2019-05-02
1    1    bad      2019-05-02
1    1    good     2019-05-02
1    0    bad      2019-05-01
0    1    good     2019-05-01
1    1    bad      2019-05-01
0    0    bad      2019-05-01

I want to calculate the percentage of 'good' from column c for each date. I know how to get the number of 'good':

SELECT COUNT(c), date FROM my_table WHERE c != 'bad' GROUP BY date;

That returns:

count    date
3        2019-05-02
1        2019-05-01

My goal is to get this:

date         perc_good
2019-05-02   25
2019-05-01   75

So I tried the following:

SELECT date, 
       (SELECT COUNT(c) 
        FROM my_table 
        WHERE c != 'bad' 
        GROUP BY date) / COUNT(c) * 100 as perc_good 
FROM my_table 
GROUP BY date;

And I get an error saying

more than one row returned by a subquery used as an expression.

I found this answer but not sure how to or if it applies to my case:

Calculating percentage in PostgreSql

How do I go about calculating the percentage for multiple rows?

avg() is convenient for this purpose:

select date,
       avg( (c = 'good')::int ) * 100 as percent_good
from t
group by date
order by date;

How does this work? c = 'good' is a boolean expression. The ::int converts it to a number, with 1 for true and 0 for false. The average is then the average of a bunch of 1s and 0s -- and is the ratio of the true values.

You could use a conditional sum for get the good value and count for total

below an exaustive code sample

  select  date
    , count(c) total 
    , sum(case when c='good' then 1 else 0 end)  total_good
    , sum(case when c='bad' then 1 else 0 end)  total_bad 
    , (sum(case when c='good' then 1 else 0 end) / count(c))* 100 perc_good
    , (sum(case when c='bad' then 1 else 0 end) / count(c))* 100 perc_bad 
  from my_table
  group by  date 

and for your result

  select  date
    , (sum(case when c='good' then 1 else 0 end) / count(c))* 100 perc_good
  from my_table
  group by  date 

or as suggested by a_horse_with_no_name using count(*) filter()

select  date
  , ((count(*) filter(where c='good'))/count(*))* 100 perc_good
from my_table
group by  date 

For this case you need to use conditional AVG() :

SELECT 
  date, 
  100 * avg(case when c = 'good' then 1 else 0 end) perc_good 
FROM my_table 
GROUP BY date;

See the demo .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM