簡體   English   中英

Postgres SQL Exclusive OR (XOR) CHECK CONSTRAINT,有可能嗎?

[英]Postgres SQL Exclusive OR (XOR) CHECK CONSTRAINT, is it possible?

是否可以進行 XOR CHECK 約束?

我正在我剛剛制作的名為test的測試表上執行此操作,該表有 3 列:

  • id,大整數
  • 一、大整數
  • b、大整數

我為此做了一個檢查約束:

(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 所以即使這仍然有效。

有人對此有解決方案嗎? 最好是三列或更多列也可以的? 我知道這些可能會更復雜。

編輯:改變

= NULL

IS NULL

給我嗎:

ERROR:  cannot cast type boolean to bigint

對,正如@a_horse_with_no_name 所指出的那樣, a = NULLb = NULL位是問題所在。 您也可以考慮這種不需要OR運算符的導數:

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

當然,這僅適用於僅兩列XOR比較。 在類似的測試表中通過三列或更多列的XOR比較,您可以采用更像這樣的類似方法:

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

您不能將 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);

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

或者,檢查約束可以簡化為

check ( num_nonnulls(a,b) = 1 )

這也更容易適應更多列

這是明確的異或。 為什么不先將其定義為布爾運算符? 它也可能對其他情況有用。

CREATE OR REPLACE FUNCTION public.xor (a boolean, b boolean) returns boolean immutable language sql AS
$$
SELECT (a and not b) or (b and not a);
$$;

CREATE OPERATOR # 
(
    PROCEDURE = public.xor, 
    LEFTARG = boolean, 
    RIGHTARG = boolean
);

然后 CHECK ((a IS NULL) # (b IS NULL))

感謝維克。 我在vue中進行了類似的測試。 在左連接中,至少 2 列或更多列不能為空。

SELECT
    (tbl1.col1 IS NOT NULL)::INTEGER +
    (tbl2.col1 IS NOT NULL)::INTEGER +
    (tbl3.col1 IS NOT NULL)::INTEGER +
    (tbl4.col1 IS NOT NULL)::INTEGER +
    (tbl5.col1 IS NOT NULL)::INTEGER +
    (tbl6.col1 IS NOT NULL)::INTEGER > 1 AS
    b_mult_cols
FROM tlb1
    LEFT JOIN tbl2 ON tlb1.col1 = tlb2.col1
    LEFT JOIN tbl3 ON tlb1.col1 = tlb3.col1
    LEFT JOIN tbl4 ON tlb1.col1 = tlb4.col1
    LEFT JOIN tbl5 ON tlb1.col1 = tlb5.col1
    LEFT JOIN tbl6 ON tlb1.col1 = tlb6.col1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM