bool_and 和 bool_or 具有 NULL 值的行为

Behaviour of bool_and and bool_or with NULL values

我正在使用聚合函数 bool_orbool_and 来聚合一些记录并查看特定列是否存在分歧。 根据 official documentation:

bool_and(expression)    true if all input values are true, otherwise false
bool_or(expression)     true if at least one input value is true, otherwise false

但是这个测试查询:

SELECT bool_or(val),bool_and(val) FROM UNNEST(array[true,NULL]::bool[]) t(val)

两列的产量 true

我认为 bool_and 排除了 NULL 值。有什么方法可以使用内置的聚合函数来进行上述查询 return true and NULL?

出于我需要做的目的,我最终使用了两个额外的列,您可以在更新的测试查询中看到它们:

SELECT
  bool_or(val),
  bool_and(val),
  bool_or(val IS NULL),
  bool_and(val IS NULL)
FROM UNNEST(array[true,NULL]::bool[]) t(val)

这足以涵盖本专栏的所有三个值。

请注意,使用 COUNT(DISTINCT val) 将不起作用,因为 NULL 未包含在内

希望对大家有所帮助!

是的,这些聚合似乎忽略了 NULL 输入。

这种愚蠢几乎可以肯定直接来自 SQL 标准(尽管我不打算 pay 0 确定)。 sum(var) 等其他标准聚合以这种方式工作,看起来它们可能只是从那里推断出来的,而没有考虑处理 null 值时算术运算和布尔运算之间的内在差异。

我认为没有任何解决方法;我相信您可以说服这些函数 return a NULL 的唯一方法是为它们提供一个空数据集。 (顺便说一句,谁坚持零行的 sum() 应该是 NULL 而不是 0 应该被提交...)

幸运的是,Postgres 是可以无限扩展的,定义您自己的聚合非常简单:

CREATE FUNCTION boolean_and(boolean, boolean) RETURNS boolean AS
  'SELECT  AND '
LANGUAGE SQL IMMUTABLE;

CREATE AGGREGATE sensible_bool_and(boolean)
(
  STYPE = boolean,
  INITCOND = true,
  SFUNC = boolean_and,
  -- Optionally, to allow parallelisation:
  COMBINEFUNC = boolean_and, 
  PARALLEL = SAFE
);

如果您只需要一次性查询,并且不想(或没有权限)向数据库添加新的聚合定义,您可以将它们放在您的连接中-通过将它们定义并引用为 pg_temp.boolean_and()/pg_temp.sensible_bool_and().
来实现本地临时模式 (如果您正在使用连接池,您可能希望在完成后删除它们。)

请注意,这比内置 bool_and() 慢约 10 倍(尽管在许多实际用例中不太可能成为瓶颈); SQL boolean 值是堆分配的且不可变的,因此 boolean_and() 需要为每次迭代分配一个新值,而 LANGUAGE C 函数允许就地更新累加器.如果性能是一个问题,并且您 willing/able 到 build and deploy your own C module, then (as with most internal functions) you can pretty easily copy-paste the bool_and() implementation 并调整它以满足您的需要。

但是除非您确实需要它,否则所有这些都有些矫枉过正。实际上,我可能会改用@Luke 的解决方案。

我的回答是:

SELECT bool_or(val),bool_and(val) FROM UNNEST(array[true,NULL]::bool[]) t(val) 
       having bool_and (val is not null)

希望对您有所帮助

我认为您可以这样做以获得所需的行为:

SELECT
  BOOL_OR(val),
  BOOL_AND(COALESCE(val, FALSE)) 
FROM
  UNNEST(array[TRUE, NULL]::bool[]) t(val)

我最终做了以下操作以获得 bool_and 那样 returns 真、假或空,如果所有值分别为真,所有值为假,一个值为空:

select
  case when count(case when val IS null then 1 end) > 0 
    then null 
      else bool_and(val) 
        end
from unnest(array[true,null]::bool[]) t(val)