我有一个带有外键的表和一个布尔值(以及一些其他与此无关的列),如下所示:
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构造,但是我无法使它工作......有什么想法吗?
答案 0 :(得分:7)
无需重新定义PostgreSQL已提供的功能:bool_and()将完成这项工作:
select bool_and(someBool)
from myTable
where someKey = $1
group by someKey;
(抱歉,现在无法测试)
答案 1 :(得分:3)
与前一个类似,但在一个查询中,这将起到作用,但是,它不是干净的,也不易理解的代码:
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子句
,这将立即获得所有响应答案 2 :(得分:2)
本周我第一次安装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
这个想法是你只需要查看一行以查看是否存在任何行,如果存在至少一行,则只需要查找,直到找到错误值以确定最终值为false(或者您到最后,这是真的)。假设你有some_key的索引,我认为性能应该是好的。
答案 3 :(得分:2)
(非常小的侧点:我认为你的函数应该被声明为STABLE而不是VOLATILE,因为它只是使用数据库中的数据来确定它的结果。)
正如有人提到的,您可以在遇到“错误”值时立即停止扫描。如果这是一种常见情况,您可以使用光标实际上激发“快速完成”:
CREATE FUNCTION do_and(key int) RETURNS boolean
STABLE LANGUAGE 'plpgsql' AS $$
DECLARE
v_selector CURSOR(cv_key int) FOR
SELECT someBool FROM myTable WHERE someKey = cv_key;
v_result boolean;
v_next boolean;
BEGIN
OPEN v_selector(key);
LOOP
FETCH v_selector INTO v_next;
IF not FOUND THEN
EXIT;
END IF;
IF v_next = false THEN
v_result := false;
EXIT;
END IF;
v_result := true;
END LOOP;
CLOSE v_selector;
RETURN v_result;
END
$$;
这种方法也意味着您只在myTable上进行一次扫描。请注意,我怀疑你需要加载和加载行才能使差异显而易见。
答案 4 :(得分:1)
您还可以使用every
,这只是bool_and
的别名:
select every(someBool)
from myTable
where someKey = $1
group by someKey;
使用every使您的查询更具可读性。举个例子,展示每天只吃苹果的人:
select personId
from personDailyDiet
group by personId
having every(fruit = 'apple');
every
在语义上与bool_and相同,但很明显every
比bool_and
更具可读性:
select personId
from personDailyDiet
group by personId
having bool_and(fruit = 'apple');
答案 5 :(得分:0)
也许使用somekey = somevalue计算'all'项目,并在布尔比较中使用它与somekey的所有'True'出现的计数?
一些未经测试的伪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
答案 6 :(得分:0)
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;
如果您不需要NULL值(当没有任何行时),只需使用以下查询:
SELECT
someKey,
MIN(someBool::int)::bool AS bar
FROM
myTable
WHERE
someKey=$1
GROUP BY
someKey
答案 7 :(得分:0)
SELECT DISTINCT ON (someKey) someKey, someBool
FROM myTable m
ORDER BY
someKey, someBool NULLS FIRST
这将为每个someKey
选择第一个有序的布尔值。
如果只有一个FALSE
或NULL
,则会先返回,这意味着AND
失败。
如果第一个布尔值是TRUE
,则此键的所有其他布尔值也为TRUE
。
与聚合不同,这将使用(someKey, someBool)
上的索引。
要返回OR
,只需撤消订购:
SELECT DISTINCT ON (someKey) someKey, someBool
FROM myTable m
ORDER BY
someKey, someBool DESC NULLS FIRST