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

时间:2017-01-26 13:28:39

标签: sql postgresql constraints xor

是否可以制作XOR CHECK CONSTRAINT?

我在我刚刚制作的测试表上做了这个名为测试并且有3列:

  • id,bigint
  • a,bigint
  • b,bigint

我为此做了一个检查约束:

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

Which apparently would work in 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

4 个答案:

答案 0 :(得分:12)

是的,b = NULLOR位是@a_horse_with_no_name指示的问题。您也可以考虑这种衍生物,它不需要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 NULL)::INTEGER + (b IS NULL)::INTEGER + (c IS NULL)::INTEGER = 1) ); 比较,您可以采用类似的方法:

db.collection.update({
 "_id" : ObjectId("5890c87051e301f17d9ba814"),
"comments.5890db366a29d63612e1f8f5":{$exists:true}
},{
$push:{
"comments.$.array":rateReviewObject.comment
}
})

答案 1 :(得分:6)

您无法将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); 

答案 2 :(得分: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
);

然后检查((a IS NULL) # (b IS NULL))

答案 3 :(得分:1)

感谢维克。我也有类似的测试。左连接中至少有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