7

次のように、外部キーとブール値(およびここでは関係のない他の列の束)を持つテーブルがあります。

CREATE TABLE myTable
(
  someKey integer,
  someBool boolean
);

insert into myTable values (1, 't'),(1, 't'),(2, 'f'),(2, 't');

各 someKey は、0 個以上のエントリを持つことができます。特定の someKey について、a) すべてのエントリが true であるか、b) いずれかのエントリが false (基本的には 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

前のものと似ていますが、1 つのクエリではこれでうまくいきますが、クリーンでも理解しやすいコードでもありません。

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

これにより、すべての応答が一度に取得されます。キーが 1 つだけ必要な場合は、WHERE 句を追加するだけです

于 2009-03-25T23:15:56.040 に答える
2

(非常にマイナーな側面:関数はデータベースからのデータを使用して結果を決定するだけなので、VOLATILEではなくSTABLEと宣言する必要があると思います。)

誰かが言ったように、「偽」の値に遭遇したらすぐにスキャンを停止できます。それが一般的なケースである場合は、カーソルを使用して実際に「高速終了」を引き起こすことができます。

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 でスキャンを 1 回だけ実行していることも意味します。気をつけてください、違いがかなりのものになるためには、たくさんの行が必要だと思います。

于 2009-05-04T12:44:30.483 に答える
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

アイデアは、存在するかどうかを確認するために 1 つの行を調べるだけでよく、少なくとも 1 つの行が存在する場合は、最終的な値が false であると判断するために false の値が見つかるまで調べるだけでよいということです (または、終わり、それは本当です)。some_key にインデックスがあると仮定すると、パフォーマンスは良好になるはずです。

于 2009-03-26T00:04:26.673 に答える
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
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失敗したことを意味します。

最初のブール値が の場合、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 に答える
0

somekey=somevalue で「すべて」の項目をカウントし、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
于 2009-03-25T23:10:44.150 に答える