在同一个SELECT sql查询中计算SUM()的百分比

时间:2013-03-26 18:28:08

标签: sql postgresql count aggregate-functions postgresql-9.1

在表my_obj中有两个整数字段:

(value_a integer, value_b integer);

我尝试计算多少时间value_a = value_b,我想以百分比表示这个比例。 这是我尝试过的代码:

select sum(case when o.value_a = o.value_b then 1 else 0 end) as nb_ok,
       sum(case when o.value_a != o.value_b then 1 else 0 end) as nb_not_ok,
       compute_percent(nb_ok,nb_not_ok)
from  my_obj as o
group by o.property_name;

compute_percent是一个只有(a * 100) / (a + b)

的stored_procedure

但是PostgreSQL抱怨列nb_ok不存在 你会怎么做到这一点?

我使用PostgreSQL 9.1和Ubuntu 12.04。

3 个答案:

答案 0 :(得分:3)

这个问题比看上去还要多。

简易版

更多更快更简单:

SELECT property_name
      ,(count(value_a = value_b OR NULL) * 100) / count(*) AS pct
FROM   my_obj
GROUP  BY 1;

结果:

property_name | pct
--------------+----
 prop_1       | 17
 prop_2       | 43

如何?

  • 根本不需要这个功能。

  • 不是计算value_b(您不需要开始)并计算总数,而是使用count(*)作为总计。更快,更简单。

  • 这假设您没有 NULL 值。即两列都定义为NOT NULL。您的问题中缺少这些信息 如果没有,您的原始查询可能没有按照您的想法执行。如果任何值为NULL,则您的版本根本不计算该行。您甚至可以通过这种方式激发除零例外 此版本也适用于NULL。无论值如何,count(*)都会生成所有行的计数。

  • 以下是计数的工作原理:

     TRUE  OR NULL = TRUE
     FALSE OR NULL = NULL
    

    count()忽略NULL值。瞧。

  • Operator precedence管辖=OR之前绑定。您可以添加括号以使其更清晰:

    count ((value_a = value_b) OR FALSE)
    
  • 您可以使用

    执行相同的操作
    count NULLIF(<expression>, FALSE)
    
  • 默认情况下,count()的结果类型为bigint 分部bigint / bigint截断小数位数

包括小数位数

使用 100.0 (带小数位)强制计算为numeric,从而保留小数位数。
您可能希望使用round()

SELECT property_name
      ,round((count(value_a = value_b OR NULL) * 100.0) / count(*), 2) AS pct
FROM   my_obj
GROUP  BY 1;

结果:

property_name | pct
--------------+-------
 prop_1       | 17.23
 prop_2       | 43.09

暂且不说:
我使用value_a代替valueA。不要在PostgreSQL中使用不带引号的混合大小写标识符。我从这种愚蠢的行为中看到了太多绝望的问题。如果您想知道我在说什么,请阅读手册中的Identifiers and Key Words章节。

答案 1 :(得分:2)

可能最简单的方法是使用with子句

WITH data 
     AS (SELECT Sum(CASE WHEN o.valuea = o.valueb THEN 1 ELSE 0 END) AS nbOk, 
                Sum(CASE WHEN o.valuea != o.valueb THEN 1 ELSE 0 END) AS nbNotOk, 
         FROM   my_obj AS o 
         GROUP  BY o.property_name) 
SELECT nbok, 
       nbnotok, 
       Compute_percent(nbok, nbnotok) 
FROM   data

答案 2 :(得分:1)

您可能还想尝试此版本:

WITH all(count) as (SELECT COUNT(*)
                    FROM my_obj),
     matching(count) as (SELECT COUNT(*)
                         FROM my_obj
                         WHERE valueA = valueB)
SELECT nbOk, nbNotOk, Compute_percent(nbOk, nbNotOk)
FROM (SELECT matching.count as nbOk, all.count - matching.count as nbNotOk
      FROM all
      CROSS JOIN matching) data