我有三个表1.帐户2.部门和3.经理 在经理表中,我需要添加帐户键和部门键作为外键,但其中一个将为空,另一个将具有值,这意味着如果插入客户经理,则部门键将为空,而帐户键将具有值。如何在postgres中添加约束
create table account(id serial primary key);
insert into account values (default);
create table dept(id serial primary key);
insert into dept values (default);
create table manager(
id serial primary key,
account_id int references account(id),
dept_id int references dept(id) );
null
,一个not null
如果您始终希望其中一个外键为
null
,另一个为 not null
,则可以使用 XOR
。它不允许两者都具有值,也不允许它们同时存在的行 null
:
alter table manager add constraint dept_xor_account
check( (account_id is null) != (dept_id is null) );
有一个 XOR
#
运算符,但仅适用于类型 bit
,所以它并不值得所有的转换:
alter table manager add constraint dept_xor_account
check(((account_id is null)::int::bit # (dept_id is null)::int::bit)::int::bool);
insert into manager values(default,1,null);--works fine
insert into manager values(default,null,1);--works fine
insert into manager values(default,1,1);--can't have both links
ERROR: new row for relation "manager" violates check constraint "dept_xor_account" DETAIL: Failing row contains (3, 1, 1).
insert into manager values(default,null,null);--can't have neither link
ERROR: new row for relation "manager" violates check constraint "dept_xor_account" DETAIL: Failing row contains (4, null, null).
null
如果你允许最多一个是
null
(一个或两个都可以是null
),你可以数一下:
alter table manager add constraint dept_or_account_or_neither
check(((account_id is not null)::int+(dept_id is not null)::int)<2);
insert into manager values(default,1,null);--works fine
insert into manager values(default,null,1);--works fine
insert into manager values(default,null,null);--works fine, CAN have neither link
insert into manager values(default,1,1);--can't have both links
ERROR: new row for relation "manager" violates check constraint "dept_or_account_or_neither" DETAIL: Failing row contains (8, 1, 1).
您的描述表明您不是在寻找“至多 1 个空值”,而是在寻找“恰好 1 个空值”。您可以通过将 num_nulls() 函数 合并到 check
约束中来确定这一点。所以(参见演示)
create table manager(
id serial primary key,
account_id int references account(id),
dept_id int references dept(id),
constraint dept_xor_account check ( num_nulls(account_id,dept_id) = 1);