2

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)

但是 PostgreSQL 抱怨该列nb_ok不存在。
你会如何正确地做到这一点?

我将 PostgreSQL 9.1 与 Ubuntu 12.04 一起使用。

4

3 回答 3

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 值。瞧。

  • 运算符优先级控制=之前的绑定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 中使用不带引号的混合大小写标识符。我已经看到太多来自这种愚蠢的绝望问题。如果您想知道我在说什么,请阅读手册中的标识符和关键字一章。

于 2013-03-27T02:22:30.707 回答
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
于 2013-03-26T18:32:02.117 回答
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
于 2013-03-26T18:47:28.163 回答