在表 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
不存在。
你会如何正确地做到这一点?
我在 Ubuntu 12.04 上使用 PostgreSQL 9.1。
最佳答案
这个问题比看起来要复杂得多。
简单版
这多更快更简单:
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
值。 IE。两列都定义为NOT NULL
。您的问题中缺少信息。
如果不是,则您的原始查询可能没有按照您的想法行事。如果任何值为 NULL,则您的版本根本不计算该行。您甚至可以通过这种方式引发被零除异常。
此版本也适用于 NULL。count(*)
生成所有行的计数,而不考虑值。计数的工作原理如下:
TRUE OR NULL = TRUE FALSE OR NULL = NULL
count()
忽略 NULL 值。瞧。Operator precedence控制
=
在OR
之前绑定(bind)。您可以添加括号以使其更清楚:count ((value_a = value_b) OR FALSE)
你可以用
做同样的事情count NULLIF(<expression>, FALSE)
count()
的结果类型默认为bigint
。
除法bigint/bigint
,截断小数位。
包括小数位
使用 100.0
(带小数位)强制计算为数字
,从而保留小数位。
您可能想使用 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在手册中。
关于sql - 在同一个 SELECT sql 查询中从 SUM() 计算百分比,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15644713/