在两个可为空的 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)
将在 a
和 b
都为非 NULL 并且 CHECK 将通过时返回非 NULL,这是不应该的(OP明确表示两者都不能有值)。换句话说,insert into #t values (1, 1)
应该会失败。
唯一的限制是算术溢出(来自a*b
),如果改为使用a|b
,则可以删除【参考方案3】:
另一种方法是在过程中定义此检查约束。在派生表中插入记录之前,应满足约束条件。否则插入失败或返回错误。
【讨论】:
以上是关于在两个可为空的 FK 之间添加 SQL XOR 约束的主要内容,如果未能解决你的问题,请参考以下文章