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

Posted

技术标签:

【中文标题】在同一个 SELECT sql 查询中从 SUM() 计算百分比【英文标题】:Compute percents from SUM() in the same SELECT sql query 【发布时间】:2013-03-16 16:47:24 【问题描述】:

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

【问题讨论】:

But postgresql complains that the column nook doesn't exist. ?请解决您的问题,任何地方都没有nook。在谈论错误消息时,请按原样将其放在您的问题中。复制/粘贴。 考虑我回答的最后一段,并点击手册的链接,了解为什么nbok 以小写形式出现。 SQL 关键字的大写完全是可选的,只是个人喜好问题。但标识符的小写字母不是。 【参考方案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 之前绑定。您可以添加括号以使其更清晰:

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 章节。

【讨论】:

这个 COUNT 是否比类似的 SUM((value_a=value_b)::integer) 或类似的 SUM(CASE...) 运行得更快? @AndrewLazarus:差异通常很小,因为与从磁盘读取数据相比,这三种形式中的每一种都非常便宜。在我上次的测试中,OR 出现在 CASE::int 之前。但是ORCASENULLIF 是“接近通话”,真的。您可以轻松地对此进行测试。这是recent similar test on dba.SE(但没有OR 版本)。【参考方案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

【讨论】:

假设一个/两个列上有一个覆盖索引,优化器是否能够将它们用于此查询(以及以何种容量),还是严格的表扫描? @Clockwork-Muse 它就像一个在线查询一样工作。它将尽其所能构建data,但之后一切都在内存中完成。 @ConradFrix 我的问题有误,我要编辑,group bygroup by o.property_name。否则就没有意义了。【参考方案3】:

您可能还想试试这个版本:

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

【讨论】:

以上是关于在同一个 SELECT sql 查询中从 SUM() 计算百分比的主要内容,如果未能解决你的问题,请参考以下文章

在 ORACLE 的 select 语句中从 PL/SQL 调用函数

在 Spark (v.1.5.2) 中从 SQL 查询创建表

带有 CASE 条件和 SUM() 的 SELECT 查询

SQL学习之组合查询(UNION)

像这样在 sum() 中使用 sql 变量--sum(@wokao) 导致无法预料的结果

SQL总结