从 count() 确定百分比没有转换问题

use*_*117 22 postgresql datatypes count cast

我正在尝试运行以下查询以提供patients表中具有refinst列值的行的百分比。我一直得到0的结果。

select (count (refinst) / (select count(*) from patients) * 100) as "Formula" 
from patients;
Run Code Online (Sandbox Code Playgroud)

该表有 15556 行,并select count(refinst) from patients告诉我其中 1446行在列中有值refinst。我想从查询中得到的响应是 30.62 ( 1446/15556*100=30.62XXXXX,四舍五入到两位小数)。

我很确定它与计数结果的数据类型(我假设是整数)有关。如果我将一个整数除以一个整数并且结果小于 0,它会被截断为 0 是否正确?如果是这种情况,有人可以告诉我如何将计数结果转换为带有 2 个小数位的数字,以便结果也四舍五入到 2 个小数位吗?

我确信有比多个 count 语句更好的方法来编写此代码。我正在寻找一种更高效的方式来编写这个查询。

Erw*_*ter 27

SELECT (count(refinst) * 100)::numeric / NULLIF(count(*), 0) AS refinst_pct
    -- count(refinst) * 100.0 / NULLIF(count(*), 0) AS refinst_pct  -- simpler
FROM   patients;
Run Code Online (Sandbox Code Playgroud)
  • 千万不能使用子查询。两个聚合都可以从同一个查询中派生出来。更便宜。

  • 此外,这不是窗口函数的情况,因为您想要计算单个结果,而不是每行一个结果。

  • 转换为任何支持小数位数的数字类型,例如@a_horse 已经解释过
    由于您想要round()两个小数位,我建议numeric(这与decimalPostgres 中的相同)。
    投射一个参与计算的值就足够了,最好是第一个。Postgres 会自动适应不会丢失信息的类型。
    或者,更简单:因为我们无论如何都要进行乘法运算,所以使用一个由于小数点 ( )而被自动强制转换的数字常量numeric100.0

  • 除法之前乘法通常是个好主意。这通常可以最大限度地减少舍入误差并且更便宜。
    在这种情况下,第一个乘法 ( count(refinst) * 100) 可以用廉价而精确的integer算术计算。只有这样我们才能投射到numeric并除以下一个integer(我们不会额外投射)。

  • NULLIF(count(*), 0)防止被零除(引发异常)。如果根本没有行,我们将 NULL 作为(未知)百分比。

四舍五入为两位小数:

SELECT round((count(refinst) * 100)::numeric / NULLIF(count(*), 0), 2) AS refinst_pct
FROM   patients;
Run Code Online (Sandbox Code Playgroud)


小智 3

您需要将除法中涉及的每个数字转换为支持小数的类型:

select (count(refinst)::decimal / (select count(*) from patients)::decimal) * 100  as "Formula" 
from patients;
Run Code Online (Sandbox Code Playgroud)

您可能还想尝试窗口函数而不是标量子查询。它可能会更快:

select (count(refinst)::decimal / (count(*) over ())::decimal) * 100 as "Formula" 
from patients;
Run Code Online (Sandbox Code Playgroud)