在表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。
答案 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