web-dev-qa-db-ja.com

Postgres SQL Exclusive OR(XOR)CHECK CONSTRAINT、それは可能ですか?

XOR CHECK CONSTRAINTを作成することは可能ですか?

testと呼ばれ、3つの列があるテストテーブルで実行しています。

  • id、bigint
  • a、bigint
  • b、bigint

これに対してチェック制約を作成しました:

(a IS NOT NULL AND b = NULL) OR (b IS NOT NULL AND a = NULL)

これは明らかにMSSQLで機能します

私はこれを行うことによってそれをテストしました:

INSERT INTO public.test(
    id, a, b)
    VALUES (1, 1, 1);

ORのどちらの側でもTRUEと評価されないため、これは失敗するはずです。しかし、それはうまく挿入しています。

Postgresが実際に制約として保存したものを見ると、次のようになります。

(a IS NOT NULL AND b = NULL::bigint OR b IS NOT NULL AND a = NULL::bigint)

ANDがORよりも優先されると聞いたので、これでも機能するはずです。

誰かがこれに対する解決策を持っていますか?できれば、3つ以上のカラムでも可能なものですか?しかし、それらはもっと複雑かもしれないことを理解しています。

編集:変更

= NULL

IS NULL

ください:

ERROR:  cannot cast type boolean to bigint
9
Blanen

NULL値を=と比較することはできません。IS NULLが必要です

(a IS NOT NULL AND b is NULL) OR (b IS NOT NULL AND a is NULL)

チェック制約の場合、式全体を括弧で囲む必要があります。

create table xor_test 
(
  id integer primary key, 
  a integer, 
  b integer, 
  check ((a IS NOT NULL AND b is NULL) OR (b IS NOT NULL AND a is NULL))
);

-- works
INSERT INTO xor_test(id, a, b) VALUES (1, null, 1);

-- works
INSERT INTO xor_test(id, a, b) VALUES (2, 1, null);

-- failse
INSERT INTO xor_test(id, a, b) VALUES (3, 1, 1); 

そうです、a = NULLおよびb = NULLビットは、@ a_horse_with_no_nameが示すように問題でした。 OR演算子を必要としないこの導関数を検討することもできます。

create table test 
(
  id integer primary key, 
  a integer, 
  b integer, 
  check ((a IS NULL) != (b IS NULL))
);

もちろん、これは2列のXOR比較でのみ機能します。同様のテストテーブルで3つ以上の列XORを比較すると、次のような同様のアプローチに頼ることができます。

create table test 
(
  id integer primary key, 
  a integer, 
  b integer, 
  c integer, 
  check ((a IS NULL)::INTEGER + 
         (b IS NULL)::INTEGER + 
         (c IS NULL)::INTEGER = 1)
);
15
Vic