如何在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
?