【问题标题】:How can I show the counts of each group in my query as percentages?如何将查询中每个组的计数显示为百分比?
【发布时间】:2017-05-07 11:08:56
【问题描述】:

我在 mytable(Movies) 中有一个专栏,演员。我正在尝试获取每个演员出现的次数百分比。

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%

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

    • 你应该使用* 100.0而不是* 1.0
    • 我已经编辑了我的原始帖子。请看一下。
    • @mcNets 。 . .我承认 OP 的值介于 0 到 100 之间。但该列称为“比率”。我认为在这种情况下,比率应该在 0 和 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()

    【讨论】:

    • 感谢 Evan,100 是一个百分比,对于 Robert DeSouza 来说是 4/9,所以 44%。我希望能够显示整体的百分比。
    • @PLearner 所以你想让它说 99?
    【解决方案3】:

    没有包含百分号(% 字符)的 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

    【讨论】:

    • 感谢 Air,我已根据您的建议编辑了原始帖子。我不知道为什么它仍然不等于 100%。它只是将所有值相加并产生 9%。知道为什么会这样。
    【解决方案4】:

    这是通过使用联合将分组结果与整体结果连接起来来实现的。

    编辑:在来自@Air 的 cmets 之后删除了串联和 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)
    

    【讨论】:

    • 它有效@Air ...它回答了这个问题。 psql 客户端中需要 order by 我必须在底部得到总数......联合解决了舍入问题。错别字...哪里...
    • 如果100.00 不是错字,那么我很想知道您添加百分之一数字的动机。就其工作而言,当然,许多错误查询在 9 行示例数据上工作得很好
    • 当我测试代码时......我认为将百分比四舍五入到 2 点......但是点数......我会编辑。串联......你是认真地说 to_char 会更好吗?您是否建议两个聚合查询的联合在某种程度上是一个坏/慢查询?你今天过得不好还是一直这样?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-12
    • 2016-04-09
    • 2022-11-16
    • 1970-01-01
    • 2012-09-08
    • 2017-12-14
    相关资源
    最近更新 更多