为什么PostgreSQL中的聚合函数不适用于布尔数据类型

时间:2013-11-29 08:16:37

标签: database postgresql rdbms

为什么我们不能在聚合函数中使用布尔值而不首先转换为某种整数类型?在许多情况下,从布尔数据类型的列计算总和,平均值或相关性是完全合理的。

考虑以下示例,其中布尔输入必须始终转换为int才能使其正常工作:

select
   sum(boolinput::int),
   avg(boolinput::int),
   max(boolinput::int),
   min(boolinput::int),
   stddev(boolinput::int),
   corr(boolinput::int,boolinputb::int)   
from
   (select 
      (random() > .5)::boolean as boolinput,
      (random() > .5)::boolean as boolinputB 
    from 
      generate_series(1,100)
   ) a

来自PostgreSQL文档:

  

“true”状态的有效文字值为:   “1”

是真的'''是''''      

对于“false”状态,可以使用以下值:   FALSE'f''false''n''no''off''0'

因为根据定义TRUE等于1FALSE等于0我不明白为什么必须进行投射。

在聚合中允许布尔值也会产生有趣的副作用 - 例如,我们可以简化许多案例陈述:

当前版本(干净且易于理解):

select sum(case when gs > 50 then 1 else 0 end) from generate_series(1,100) gs;

使用旧式投射操作符::

select sum((gs > 50)::int) from generate_series(1,100) gs;

布尔值的直接聚合(当前不工作):

select sum(gs > 50) from generate_series(1,100) gs;

是否可以在其他DBMS中直接聚合布尔值?为什么在PostgreSQL中无法做到这一点?

4 个答案:

答案 0 :(得分:6)

  

因为根据定义,TRUE等于1而FALSE等于0,我不明白为什么需要进行强制转换。

根据您在问题中引用的文档,根据定义,布尔值不是1表示TRUE,0表示FALSE。在C中也不是这样,其中TRUE是非零值。

就此而言,也不是在这方面模仿C的语言,其中有很多。也不是像Ruby这样的语言,其中任何非Nil / non-False评估为True,包括零和空字符串。它也不适用于POSIX shell及其变体,其中测试返回码如果为零则产生TRUE,而对于任何非零值则产生FALSE。

点是,布尔值是一个布尔值,具有从平台到下一个平台的各种丰富的实现细节;不是整数。

目前还不清楚你是如何期待Postgres平均真假值的。我怀疑许多平台是否会产生结果。

即使汇总布尔值也很尴尬:期望Postgres输入值为OR,还是计算TRUE值?

无论如何,有一些布尔聚合函数,即bool_or()bool_and()。这些替换了更标准的any()some()。 Postgres偏离标准的原因是由于潜在的模糊性。根据文档:

SELECT b1 = ANY((SELECT b2 FROM t2 ...)) FROM t1 ...;
  

如果子查询返回一个带有布尔值的行,则可以将ANY视为引入子查询或者作为聚合函数。

http://www.postgresql.org/docs/current/static/functions-aggregate.html

答案 1 :(得分:0)

为了对布尔值求和,我创建了以下自定义聚合函数:

create or replace function badd (bigint, boolean)
  returns bigint as
$body$
select $1 + case when true then 1 else 0 end;
$body$ language sql;

create aggregate sum(boolean) (
  sfunc=badd,
  stype=int8,
  initcond='0'
);

现在我可以轻松地对布尔值求和或计算满足特定条件的行:

with test (a, b, c) as (
   values
      ('true'::boolean,'a'::varchar, 'd'::text),
      ('true'::boolean,'a'::varchar, 'e'::text),      
      ('false'::boolean,'a'::varchar, 'f'::text),
      ('true'::boolean,'b'::varchar, 'd'::text),
      ('false'::boolean,'b'::varchar, 'd'::text),
      ('true'::boolean,'c'::varchar, 'f'::text),                
      (NULL,'c'::varchar,'d')      
    ) 
select 
   b,
   bsum(a) as sum, -- sum boolean value (TRUE=1, FALSE=0)
   bsum(c = 'd') as dsum -- counts all rows where column c equals to value 'd'
from 
   test
group by
   b

答案 2 :(得分:0)

以下是如何实现max(boolean)

CREATE AGGREGATE max(boolean) (
  SFUNC=boolor_statefunc,
  STYPE=bool,
  SORTOP=">"
);  

其中" boolor_statefunc"内置于功能

答案 3 :(得分:0)

另一个选项是ARRAY_AGG

SELECT 
  id,
  true = ANY(ARRAY_AGG(flag)) AS flag
FROM my_table
GROUP BY id