DISTINCT FROMは、何らかの方法ですべてまたはすべてと組み合わせることができますか?


13

組み合わせのPostgresの道であるIS DISTINCT FROMANYかと同じ結果を得るための他のいくつかのきちんとした方法は?

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo <> any(array[null, 'A']);

 count
-------
     1
(1 row)

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo is distinct from any(array[null, 'A']);  

ERROR:  syntax error at or near "any"
LINE 3: where foo is distinct from any(array[null, 'A']);
                                   ^

回答:


7

おそらくこのように

select foo
     , exists (values (null), ('A') except select foo) chk_any
     , not exists (values (null), ('A') intersect select foo) chk_all
from ( values ('A'),('Z'),(null) ) z(foo);

 foo | chk_any | chk_all
-----+---------+---------
 A   | t       | f
 Z   | t       | t
     | t       | f

null「配列」だけでなく、nullin もzこの方法で比較されていることに注意してください。


13

それを文法問題として見ると、ANY次のように定義されます(行と配列の比較)。

式演算子ANY(配列式)

しかしis distinct from、演算子ではなく、比較演算子で説明されているように、「構築」です。

この動作が適切でない場合は、IS [NOT] DISTINCT FROM 構文を使用します

PostgreSQLにはユーザー定義の演算子があるため、この目的のために演算子/関数の組み合わせを定義できます。

create function is_distinct_from(text, text) returns bool as 
'select $1 is distinct from $2;' language sql;

create operator <!> (
 procedure=is_distinct_from(text,text),
 leftarg=text, rightarg=text
);

次に、それが先行することができますANY

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo <!> any(array[null, 'A']);  
 カウント 
-------
     3
(1行)

1
優れた洞察に満ちた答え。
アーウィンブランドステッター14年

これは、特に@Erwinの改善により、私が提案した回避策よりも明らかに優れています。
アンドリーM 14年

この答えと@Erwinが提案した調整は本当に優れています。私はアンドリーのものを受け入れていますが、それは個人的な好みの例です。多くの人があなたの優雅さを好むと確信しています。
ジャックはtopanswers.xyzを試す14年

@JackDouglas:標準演算子を使用した代替ソリューションを追加しました。
アーウィンブランドステッター14年

それは残念です...すべての意図と目的のためにIS DISTINCT FROM、演算子であるべきではありませんか?セマンティックの問題というよりも、パーサーの技術的な制限のようです。
アンディ

10

オペレーター

これは@Danielの巧妙な演算子に基づいています。
その間、多相型を使用して関数/演算子のコンボを作成します。次に、すべてのタイプで機能します -コンストラクトのように。
そして、関数を作成しますIMMUTABLE

CREATE FUNCTION is_distinct_from(anyelement, anyelement)
  RETURNS bool LANGUAGE sql IMMUTABLE AS 
'SELECT $1 IS DISTINCT FROM $2';

CREATE OPERATOR <!> (
  PROCEDURE = is_distinct_from(anyelement,anyelement),
  LEFTARG  = anyelement
, RIGHTARG = anyelement
);

symbolhoundを使用したクイック検索が空になったため、オペレーター<!>はどのモジュールでも使用されていないようです。

場合あなたは、この演算子をたくさん使用しようとしている、あなたは(問い合わせプランナを支援するためにいくつかのより多くのそれを肉付けかもしれないlosthorseコメントで提案されているように)。まず、クエリオプティマイザーを支援するためにCOMMUTATORand NEGATOR句を追加できます。CREATE OPERATOR上からこれを置き換えます:

CREATE OPERATOR <!> (
  PROCEDURE = is_distinct_from(anyelement,anyelement),
  LEFTARG  = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);

追加します:

CREATE FUNCTION is_not_distinct_from(anyelement, anyelement)
  RETURNS bool LANGUAGE sql IMMUTABLE AS 
'SELECT $1 IS NOT DISTINCT FROM $2';

CREATE OPERATOR =!= (
  PROCEDURE = is_not_distinct_from(anyelement,anyelement),
  LEFTARG  = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = =!=
, NEGATOR = <!>
);

ただし、追加の句は、当面のユースケースには役立たず、プレーンインデックスはまだ使用されません。それを達成するためにはるかに洗練されています。(試していません。)詳細については、マニュアルの「オペレーター最適化情報」の章をお読みください。

テストケース

質問のテストケースは、配列内のすべての値が同一である場合にのみ成功します。質問('{null,A}'::text[])の配列の場合、結果は常にTRUEです。それは意図したものですか?「IS DISTINCT FROM ALL」のテストを追加しました:

SELECT foo
     , foo <!> ANY ('{null,A}'::text[]) AS chk_any
     , foo <!> ALL ('{null,A}'::text[]) AS chk_all
FROM (
   VALUES ('A'),('Z'),(NULL)
   ) z(foo)

 foo | chk_any | chk_all
-----+---------+---------
 A   | t       | f
 Z   | t       | t
     | t       | f

標準演算子による代替

foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax

ほとんどに翻訳することができます

foo = ALL (test_arr) IS NOT TRUE

foo = ALL (test_arr) 収量...

TRUE ..すべての要素がfoo
FALSE..である場合いずれかのNOT NULL要素が<> foo
NULL ..であり、少なくとも1つの要素がIS NULLあり、要素がない場合<> foo

だから、残りのコーナーケースはどこに
- foo IS NULL
- test_arr何が、で構成されていNULL要素。

いずれかを除外できる場合は、完了です。したがって、次の場合は単純なテストを使用します
-列が定義されている場合NOT NULL
- またはあなたが知っている配列がすべてのNULLになることはありません。

それ以外の場合、さらにテストします。

AND ('A' = ALL(test_arr) IS NOT NULL OR 
     'B' = ALL(test_arr) IS NOT NULL OR
     foo IS NOT NULL)

場所'A''B'個別の値です。SOに関するこの関連質問の下の説明と代替案:
配列はPostgreSQLのすべてNULLです

繰り返しますtest_arr、たとえば空の文字列''などに存在できない値について知っている場合は、引き続き単純化できます。

AND ('' = ALL(test_arr) IS NOT NULL OR
     foo IS NOT NULL)

すべての組み合わせを確認するための完全なテストマトリックスを次に示します。

SELECT foo, test_arr
     , foo = ALL(test_arr) IS NOT TRUE  AS test_simple
     , foo = ALL(test_arr) IS NOT TRUE
       AND ('A' = ALL(test_arr) IS NOT NULL OR
            'B' = ALL(test_arr) IS NOT NULL OR 
            foo IS NOT NULL)            AS test_sure 
FROM (
   VALUES ('A'),('Z'),(NULL)
   ) v(foo)
CROSS JOIN (
   VALUES ('{null,A}'::text[]),('{A,A}'),('{null,null}')
   ) t(test_arr)

 foo |  test_arr   | test_simple | test_sure
-----+-------------+-------------+-----------
 A   | {NULL,A}    | t           | t
 A   | {A,A}       | f           | f   -- only TRUE case
 A   | {NULL,NULL} | t           | t
 Z   | {NULL,A}    | t           | t
 Z   | {A,A}       | t           | t
 Z   | {NULL,NULL} | t           | t
     | {NULL,A}    | t           | t
     | {A,A}       | t           | t
     | {NULL,NULL} | t           | f   -- special case

これはAndriyのEXCEPTソリューションよりも少し冗長ですが、かなり高速です。


を作成するときにOPERATORCOMMUTATOR(およびNEGATOR、おそらく逆IS NOT DISTINCT FROM演算子を使用して)句を指定する必要がありますか?postgresql.org/docs/current/static/xoper-optimization.html
losthorse

1
@losthorse:それに対処するビットを追加しました。
アーウィンブランドステッター

この演算子を使用して、このようなapp_status(整数)に基づいてレコードを削除していapp_status <!> any(array[3,6])ます。残念ながら、レコードには影響しません。整数で動作しますか?
M.ハビブ

@ M.Habib:新しい質問として質問してください。(関連するすべての詳細があります!)コンテキストのためにいつでもこのリンクにリンクできます-ここにコメントをドロップしてリンクします。
アーウィンブランドステッター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.