7

我有一个带有外键和布尔值的表(以及一堆在这里不相关的其他列),例如:

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 构造,但我无法让它工作......有什么想法吗?

4

8 回答 8

7

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

select bool_and(someBool)
  from myTable
  where someKey = $1
  group by someKey;

(抱歉,现在无法测试)

于 2009-05-04T12:02:44.157 回答
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 子句

于 2009-03-25T23:15:56.040 回答
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 上有一个索引,我认为性能应该不错。

于 2009-03-26T00:04:26.673 回答
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 进行一次扫描。请注意,我怀疑您需要大量的行才能使差异明显。

于 2009-05-04T12:44:30.483 回答
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');
于 2012-05-23T08:28:38.027 回答
0

也许用 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
于 2009-03-25T23:10:44.150 回答
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
于 2009-03-26T13:24:44.253 回答
0
SELECT  DISTINCT ON (someKey) someKey, someBool
FROM    myTable m
ORDER BY
        someKey, someBool NULLS FIRST

这将为每个 选择第一个有序布尔值someKey

如果有 singleFALSE或 a NULL,会先返回,表示AND失败。

如果第一个布尔值是 a TRUE,那么所有其他布尔值也TRUE适用于这个键。

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

要返回OR,只需颠倒顺序:

SELECT  DISTINCT ON (someKey) someKey, someBool
FROM    myTable m
ORDER BY
        someKey, someBool DESC NULLS FIRST
于 2009-05-31T14:20:03.850 回答