如何在postgresql中对未知数量的布尔值执行AND?

如何在postgresql中对未知数量的布尔值执行AND?,sql,postgresql,plpgsql,Sql,Postgresql,Plpgsql,我有一个带有外键和布尔值的表(以及一堆其他与此无关的列),如下所示: CREATE TABLE myTable ( someKey integer, someBool boolean ); insert into myTable values (1, 't'),(1, 't'),(2, 'f'),(2, 't'); 每个someKey可以有0个或多个条目。对于任何给定的someKey,我需要知道a)所有条目是真的,还是b)任何条目是假的(基本上是AND) 我提出了以下功能: CREA

我有一个带有外键和布尔值的表(以及一堆其他与此无关的列),如下所示:

CREATE TABLE myTable
(
  someKey integer,
  someBool boolean
);

insert into myTable values (1, 't'),(1, 't'),(2, 'f'),(2, 't');
每个someKey可以有0个或多个条目。对于任何给定的someKey,我需要知道a)所有条目是真的,还是b)任何条目是假的(基本上是AND)

我提出了以下功能:

CREATE FUNCTION do_and(int4) RETURNS boolean AS
$func$
declare
    rec record;
    retVal boolean = 't'; -- necessary, or true is returned as null (it's weird)
begin
    if not exists (select someKey from myTable where someKey = $1) then
        return null; -- and because we had to initialise retVal, if no rows are     found true would be returned
    end if;

    for rec in select someBool from myTable where someKey = $1 loop
        retVal := rec.someBool AND retVal;
    end loop;

    return retVal;
end;
$func$ LANGUAGE 'plpgsql' VOLATILE;
。。。这给出了正确的结果:

select do_and(1) => t
select do_and(2) => f
select do_and(3) => null

我想知道是否有更好的方法。在这个简单的场景中,它看起来不太糟糕,但一旦包含了所有支持代码,它就会比我想要的更长。我曾考虑过将someBool列强制转换为数组并使用ALL构造,但无法使其正常工作。。。有什么想法吗?

可以用somekey=somevalue来计算“所有”项,并将其用于与somekey的所有“真”出现次数进行布尔比较

一些未经测试的伪sql来说明我的意思

select foo1.count_key_items = foo2.count_key_true_items
from
   (select count(someBool) as count_all_items from myTable where someKey = '1') as foo1,
   (select count(someBool) as count_key_true_items from myTable where someKey = '1' and someBool) as foo2

与前一个类似,但在一个查询中,这将达到目的,但是,它既不干净,也不容易理解代码:

SELECT someKey, 
  CASE WHEN sum(CASE WHEN someBool THEN 1 ELSE 0 END) = count(*)
                    THEN true 
                    ELSE false END as boolResult
FROM  table
GROUP BY someKey

这将一次获得所有响应,如果您只需要一个键,只需添加一个WHERE子句

我刚刚在本周首次安装了PostgreSQL,因此您需要清理语法,但这里的总体思路应该是可行的:

return_value = NULL

IF EXISTS
(
     SELECT
          *
     FROM
          My_Table
     WHERE
          some_key = $1
)
BEGIN
     IF EXISTS
     (
          SELECT
               *
          FROM
               My_Table
          WHERE
               some_key = $1 AND
               some_bool = 'f'
     )
          SELECT return_value = 'f'
     ELSE
          SELECT return_value = 't'
END
其思想是,您只需查看一行,以查看是否存在任何行,如果至少存在一行,则只需查看,直到找到一个假值,以确定最终值是否为假(或者您到达末尾,结果为真)。假设您在某个_键上有一个索引,我认为性能应该是好的

CREATE FUNCTION do_and(int4)
  RETURNS boolean AS
$BODY$
  SELECT
    MAX(bar)::bool
  FROM (
    SELECT
      someKey,
      MIN(someBool::int) AS bar
    FROM
      myTable
    WHERE
      someKey=$1
    GROUP BY
      someKey

    UNION

    SELECT
      $1,
      NULL
  ) AS foo;
$BODY$
  LANGUAGE 'sql' STABLE;
如果不需要空值(当没有任何行时),只需使用以下查询:

SELECT
  someKey,
  MIN(someBool::int)::bool AS bar
FROM
  myTable
WHERE
  someKey=$1
GROUP BY
  someKey

无需重新定义PostgreSQL已经提供的函数:()将完成以下工作:

select bool_and(someBool)
  from myTable
  where someKey = $1
  group by someKey;
(很抱歉,现在无法测试它)

(非常次要的一点:我认为您的函数应该声明为稳定的,而不是易失的,因为它只使用数据库中的数据来确定其结果。)

正如有人提到的,一旦遇到“false”值,就可以停止扫描。如果这是一种常见情况,您可以使用光标实际触发“快速完成”:

这种方法还意味着您只需对myTable进行一次扫描。请注意,我怀疑您需要大量的行,以便使差异变得明显

SELECT  DISTINCT ON (someKey) someKey, someBool
FROM    myTable m
ORDER BY
        someKey, someBool NULLS FIRST
这将为每个
someKey
选择第一个有序布尔值

如果存在单个
FALSE
NULL
,则会首先返回,这意味着
失败

如果第一个布尔值是
TRUE
,则此键的所有其他布尔值也是
TRUE

与聚合不同,它将使用
(someKey,someBool)
上的索引

要返回
,只需颠倒顺序:

SELECT  DISTINCT ON (someKey) someKey, someBool
FROM    myTable m
ORDER BY
        someKey, someBool DESC NULLS FIRST

您还可以使用
every
,它只是
bool_和
的别名:

select every(someBool)
from myTable
where someKey = $1
group by someKey;
select personId
from personDailyDiet
group by personId
having bool_and(fruit = 'apple');
使用every可以使查询更具可读性。举个例子,展示所有每天只吃苹果的人:

select personId
from personDailyDiet
group by personId
having every(fruit = 'apple');
each
在语义上与bool\u和相同,但显然
each
bool\u和
更具可读性:

select every(someBool)
from myTable
where someKey = $1
group by someKey;
select personId
from personDailyDiet
group by personId
having bool_and(fruit = 'apple');

这只是一个开始,但它不处理id不存在的情况(返回true,因为0=0)。好吧,类似这样的方法应该可以解决问题,但我更喜欢另一种解决方案;-)谢谢从选择列表中删除某个键会给出正确的结果:如果你发现任何“错误”,那么“和游戏”就开始了。我改变了主意-我更喜欢这个解决方案。您需要删除“groupbysomekey”子句。使用光标和手动扫描允许您在遇到“false”时立即停止扫描,但这是一个很小的改进,与使用函数包装查询的不便相比,这不太值得。我不认为聚合可以像那样“快速停止”。是否定义了
somebool
notnull