Jim*_*art 6 postgresql normalization foreign-key database-design constraint
鉴于以下表格:
CREATE TABLE verified_name (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
UNIQUE (name, email)
);
CREATE TABLE address (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
verified_name_id INTEGER NULL REFERENCES verified_name(id)
);
Run Code Online (Sandbox Code Playgroud)
如何添加附加约束,当address.verified_name_id
不为 NULL 时,name
和email
字段address
必须与引用的字段匹配verified_name
?
我尝试将以下内容添加到address
:
FOREIGN KEY (name, email) REFERENCES verified_name(name, email)
Run Code Online (Sandbox Code Playgroud)
...但即使verified_name_id
是 NULL ,该约束也会被应用。
我正在寻找类似于部分索引语法的东西,带有像这样的子句WHERE verified_name_id IS NOT NULL
,但是简单地将这样的子句附加到FOREIGN KEY
约束中是行不通的。
当前不受欢迎的解决方案:
我可以将以下约束添加到verified_name
:
UNIQUE (name, email),
UNIQUE (id, name, email)
Run Code Online (Sandbox Code Playgroud)
以及以下约束address
:
FOREIGN KEY (verified_name_id, name, email) REFERENCES verified_name(id, name, email)
Run Code Online (Sandbox Code Playgroud)
...但这会产生一个额外的约束verified_name
,我不想有(这是一个有效的逻辑约束,但它也是多余的,并且有轻微的性能影响)。
Erw*_*ter 13
问题的核心是数据模型。在规范化模式中,您不会冗余地存储name
和email
。看起来像这样:
CREATE TABLE name (
name_id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
verified BOOLEAN NOT NULL DEFAULT FALSE,
UNIQUE (name, email)
);
CREATE TABLE address (
address_id SERIAL PRIMARY KEY,
name_id INT REFERENCES name(name_id)
...
);
Run Code Online (Sandbox Code Playgroud)
如果应该允许尚未验证的名称打破 UNIQUE 约束,您可以将其替换为部分 UNIQUE INDEX(就像您想到的那样):
CREATE UNIQUE INDEX name_verified_idx ON name(name, email) WHERE verified;
Run Code Online (Sandbox Code Playgroud)
虽然坚持您不幸的设计,但您已经发现自己的解决方案完全符合您的要求。一个FOREIGN KEY
与默认的MATCH SIMPLE
行为匹配
带有像
WHERE verified_name_id IS NOT NULL
.
引用手册:
MATCH SIMPLE
允许任何外键列为空;如果它们中的任何一个为空,则该行不需要在引用的表中具有匹配项。
一个(不太可靠和更昂贵的)替代方法是在 INSERT / UPDATE in 上的address
触发器和在 INSERT / UPDATE / DELETE in 上的触发器verified_name
。