在两个可为空的 FK 之间添加 SQL XOR 约束

Posted

技术标签:

【中文标题】在两个可为空的 FK 之间添加 SQL XOR 约束【英文标题】:Add a SQL XOR Constraint between two nullable FK's 【发布时间】:2012-06-20 20:07:28 【问题描述】:

我想在表中定义两个可为空的 FK 之间的约束,如果一个为空,则另一个需要一个值,但两者都不能为空且两者都不能有值。逻辑是派生表从任一 FK 表中继承数据以确定其类型。另外,为了有趣的奖励积分,这是一个坏主意吗?

【问题讨论】:

You could also consider the super type / sub type pattern shown here 【参考方案1】:

实现它的一种方法是简单地写下“异或”的实际含义:

CHECK (
    (FK1 IS NOT NULL AND FK2 IS NULL)
    OR (FK1 IS NULL AND FK2 IS NOT NULL)
)

但是,如果你有很多 FK,上面的方法很快就会变得笨拙,在这种情况下你可以这样做:

CHECK (
    1 = (
        (CASE WHEN FK1 IS NULL THEN 0 ELSE 1 END)
        + (CASE WHEN FK2 IS NULL THEN 0 ELSE 1 END)
        + (CASE WHEN FK3 IS NULL THEN 0 ELSE 1 END)
        + (CASE WHEN FK4 IS NULL THEN 0 ELSE 1 END)
        ...
    )
)

顺便说一句,该模式有合法用途,例如this one(尽管由于缺少延迟约束而不适用于 MS SQL Server)。在你的具体情况下是否合法,我无法根据你目前提供的信息来判断。

【讨论】:

更简单:CHECK(fk1 IS NULL != fk2 IS NULL) @StephenJ.Fuhry 遗憾的是,MS SQL Server 不将布尔类型视为一等公民,因此不会接受这种语法。 但它确实在 PostgreSQL 中工作,这正是我需要的。感谢您的简短表达,@StephenJ.Fuhry。当然,最好有异或。考虑到有多少其他东西被堆积到系统中,这似乎是一个奇怪的遗漏。【参考方案2】:

你可以使用check constraint:

create table #t (
   a int,
   b int);

alter table #t add constraint c1 
check ( coalesce(a, b) is not null and a*b is null );

insert into #t values ( 1,null);

insert into #t values ( null ,null);

跑步:

The INSERT statement conflicted with the CHECK constraint "c1". 

【讨论】:

coalesce(a, b) 将在 ab 都为非 NULL 并且 CHECK 将通过时返回非 NULL,这是不应该的(OP明确表示两者都不能有值)。换句话说,insert into #t values (1, 1) 应该会失败。 唯一的限制是算术溢出(来自a*b),如果改为使用a|b,则可以删除【参考方案3】:

另一种方法是在过程中定义此检查约束。在派生表中插入记录之前,应满足约束条件。否则插入失败或返回错误。

【讨论】:

以上是关于在两个可为空的 FK 之间添加 SQL XOR 约束的主要内容,如果未能解决你的问题,请参考以下文章

在 PySpark 数据框中添加可为空的列

为啥我不能将 DBNull.Value 插入到 sql server 2005 中的可为空的图像字段中?

SqlCeCommand 可为空的值

在 PSQL 中为可为空的列添加唯一约束

在一个管道中链接两个可为空的 Optional

将可为空的列作为参数传递给 Spark SQL UDF