XOR CHECK CONSTRAINTを作成することは可能ですか?
testと呼ばれ、3つの列があるテストテーブルで実行しています。
これに対してチェック制約を作成しました:
(a IS NOT NULL AND b = NULL) OR (b IS NOT NULL AND a = NULL)
私はこれを行うことによってそれをテストしました:
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
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)
);