如何以查询百分比的形式显示查询中每个组的计数?

时间:2016-12-21 22:07:51

标签: sql postgresql aggregate-functions window-functions to-char

我在mytable(电影),Actors中有一个专栏。我试图让每个演员出现的次数达到百分比。

CREATE TABLE movies AS SELECT * FROM ( VALUES
  ('Robert DeSouza'),
  ('Tony Wagner'),
  ('Sean Cortese'),
  ('Robert DeSouza'),
  ('Robert DeSouza'),
  ('Tony Wagner'),
  ('Sean Cortese'),
  ('Charles Bastian'),
  ('Robert DeSouza')
) AS t(actors);

我要求的结果:

select Actors, (some formula * 100) as "The Ratio" from Movies

Actors                       The Ratio
Robert DeSouza                 44%
Tony Wagner                    22%
Sean Cortese                   22%
Charles Bastian                11%
                               100%

4 个答案:

答案 0 :(得分:2)

您可以使用window functions执行此操作。对于数值计算:

select m.actor,
       count(*) * 1.0 / sum(count(*)) over () as ratio
from movies m
group by m.actor;

您可以将比率转换为您想要的任何格式 - 乘以100得到百分比,使用字符串连接来添加百分比。对我来说,称为比率的东西应该在0和1之间(在这种情况下)。

答案 1 :(得分:2)

SELECT actors, floor(count(*) *100 / sum(count(*)) OVER ())
FROM movies
GROUP BY actors
ORDER BY count(*) DESC;

这可以让你在那里大部分时间..

     actors      | floor 
-----------------+-------
 Robert DeSouza  |    44
 Tony Wagner     |    22
 Sean Cortese    |    22
 Charles Bastian |    11

不确定你的例子中你是如何得到100的。你想要百分比的底线,你想要它神奇地说100?如果今天是44+22+22+11 = 100那么那只是其中的一天。但我们也可以这样做。

SELECT actors AS "Actors", r::text || '%' AS "The Ratio"
FROM (
  SELECT
    actors AS "Actors",
    floor(count(*) *100 / sum(count(*)) OVER ()) AS r,
    false AS is_total
  FROM movies
  GROUP BY actors
  UNION ALL
    SELECT *
    FROM ( VALUES
      (null, 100, true)
    ) AS t(actors, floor, is_total)
  ORDER BY 3, 2 DESC
) AS t(actors,r);

输出,

     Actors      | The Ratio 
-----------------+-----------
 Robert DeSouza  | 44%
 Tony Wagner     | 22%
 Sean Cortese    | 22%
 Charles Bastian | 11%
                 | 100%

如果您不想floor,可以round()

答案 2 :(得分:1)

没有numeric type包含百分号(%字符),因此您的问题无法仅通过计算数值的表达式来解决。除了计算该值之外,还需要format it as text using the to_char() function

此函数采用数值并使用您提供的格式文字将其转换为文本值作为第二个参数。在这种情况下,您希望做的是舍入到最接近的百分比并显示百分号。您可能希望使用'990%'作为格式文字。将此添加到示例表和the window function that Gordon suggested会产生:

[local] air@postgres=> CREATE TABLE movies AS SELECT * FROM ( VALUES
...   ('Robert DeSouza'),
...   ('Tony Wagner'),
...   ('Sean Cortese'),
...   ('Robert DeSouza'),
...   ('Robert DeSouza'),
...   ('Tony Wagner'),
...   ('Sean Cortese'),
...   ('Charles Bastian'),
...   ('Robert DeSouza')
... ) AS t(actors);

SELECT 9
Time: 715.613 ms
[local] air@postgres=> select actors, to_char(100 * count(*) / sum(count(*)) over (), '990%') as "The Ratio" from movies group by actors;
┌─────────────────┬───────────┐
│     actors      │ The Ratio │
├─────────────────┼───────────┤
│ Charles Bastian │   11%     │
│ Tony Wagner     │   22%     │
│ Sean Cortese    │   22%     │
│ Robert DeSouza  │   44%     │
└─────────────────┴───────────┘

(4 rows)

Time: 31.501 ms

您希望确保需要显示全部可能的值,包括100%和0%;由于to_char()将四舍五入以满足您所需的精度,因此尽管表中存在,但演员有可能将其显示为零:

[local] air@postgres=> delete from movies where actors <> 'Tony Wagner';
DELETE 7
Time: 36.697 ms
[local] ahuth@postgres=> insert into movies (actors) select 'Not Tony Wagner' from generate_series(1,500);
INSERT 0 500
Time: 149.022 ms
[local] ahuth@postgres=> select actors, to_char(100 * count(*) / sum(count(*)) over (), '990%') as "The Ratio" from movies group by actors;
┌─────────────────┬───────────┐
│     actors      │ The Ratio │
├─────────────────┼───────────┤
│ Tony Wagner     │    0%     │
│ Not Tony Wagner │  100%     │
└─────────────────┴───────────┘
(2 rows)

Time: 0.776 ms

如果要将其展开以显示小数位,只需修改格式字符串即可。如果要强制前导或尾随零,请在格式文字中使用0

答案 3 :(得分:0)

这可以通过使用union将分组结果与整体结果相结合来实现。

编辑:在@Air的评论后删除连接和0。

select actors actor, ratio from ( 
    select 0 sort
    , actors
    , round(count(*) * 100.0 / ( select count(*) 
    from movies ),0) ratio 
    from movies 
    group by actors 
    union 
    select 1 sort
    , 'Total'
    , round(count(*) / count(*) * 100,0) ratio 
    from movies 
) actors 
order by sort, actors ; 

- 结果

      actor      | ratio 
-----------------+-------
 Charles Bastian | 11
 Robert DeSouza  | 44
 Sean Cortese    | 22
 Tony Wagner     | 22
 Total           | 100
(5 rows)