PostgreSQL:2个表之间的完整性约束
PostgreSQL: Integrity constraint between 2 tables
我正在使用 PostgreSQL 并且有以下 2 个表格:
create table anomaly (
id bigint not null unique,
description text not null,
zone1 varchar(100) not null,
language1 varchar(100) not null,
is_translation_anomaly boolean not null,
primary key(id)
);
create table translation_anomaly (
id bigint not null unique,
zone2 varchar(100) not null,
language2 varchar(100) not null,
primary key(id),
foreign key(id) references anomaly(id)
);
翻译异常 (translation_anomaly
) 是 anomaly
的规范。我想在不使用触发器或存储过程的情况下实现以下两个完整性约束:
- zone1 必须不同于 zone2
- 语言 1 必须不同于语言 2
我已经查看了两个 and this 问题,但无法从我可以应用于我的案例的答案中得到任何信息。我是 SQL 的新手,所以如果我在这些答案中遗漏了一些我本应了解的内容,我深表歉意。
我正在使用 PostgreSQL 9.4.10.
提前致谢!
在阅读了大量文档并向我的老师寻求帮助后,我得出的结论是 不可能 在不使用触发器的情况下实现上述完整性约束或存储过程。如果您确实需要求助于那些,官方 PostgreSQL 文档非常好:)
我怀疑你的 table 设计不正确 normalized。我不知道你的业务问题的语义,所以我只能猜测。
警告:此处的代码未经测试;语法可能不正确。
标准化
如果 child table 代表本地化中的翻译,那么包括原文在内的所有值都应该出现在 child table 中。
例如,如果您打算在 parent table 中存储英语措辞,而 child table 中存储法语和阿拉伯语,请不要这样做. child table 中应包含所有三种语言(英语、法语、阿拉伯语)。
顺便说一句,每个 table 都需要自己的唯一标识符。所以你的 child table 需要两个字段,一个是它自己的标识符(主键),另一列保存它的 parent (foreign key) 的标识符。
请注意以下代码中我们如何:
- 从第一个 table 中删除了
zone
和 language
列。
- 向
translation_anomaly
的 child table 添加了标识符列。
CREATE TABLE anomaly
(
id BIGINT NOT NULL UNIQUE,
description TEXT NOT NULL,
is_translation_anomaly BOOLEAN NOT NULL,
PRIMARY KEY (id)
)
;
CREATE TABLE translation_anomaly
(
id BIGINT NOT NULL UNIQUE,
anomaly_id BIGINT NOT NULL,
zone VARCHAR(100) NOT NULL,
language VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (anomaly_id) REFERENCES anomaly (id)
)
;
Multi-column 唯一约束
- 为
anomaly_id
加 zone
列的组合定义唯一索引,以 执行您的第一条规则 。
- 同上
language
加上 anomaly_id
以 执行您的第二条规则 。
参见问题,In Postgresql, force unique on combination of two columns。
顺便说一句,无需在标有 PRIMARY KEY
约束的列上声明 NOT NULL
或 UNIQUE
。请参阅文档页面 Constraints。
Adding a primary key will automatically create a unique B-tree index on the column or group of columns listed in the primary key, and will force the column(s) to be marked NOT NULL.
注意以下代码中我们:
- 添加了一对
UNIQUE
约束
- 从标识符列的声明中删除了多余的
NOT NULL
& UNIQUE
。
CREATE TABLE anomaly
(
id BIGINT,
description TEXT NOT NULL,
is_translation_anomaly BOOLEAN NOT NULL,
PRIMARY KEY (id)
)
;
CREATE TABLE translation_anomaly
(
id BIGINT,
anomaly_id BIGINT NOT NULL,
zone VARCHAR(100) NOT NULL,
language VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (anomaly_id) REFERENCES anomaly (id),
UNIQUE (zone, anomaly_id),
UNIQUE (language, anomaly_id)
)
;
如果您的真实规则是每个区域只有一种语言用于任何一种异常,则创建三列 UNIQUE
约束:UNIQUE ( language , zone , anomaly_id )
.
CREATE TABLE anomaly
(
id BIGINT,
description TEXT NOT NULL,
is_translation_anomaly BOOLEAN NOT NULL,
PRIMARY KEY (id)
)
;
CREATE TABLE translation_anomaly
(
id BIGINT,
anomaly_id BIGINT NOT NULL,
zone VARCHAR(100) NOT NULL,
language VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (anomaly_id) REFERENCES anomaly (id),
UNIQUE (zone, language, anomaly_id)
)
;
顺便说一下,您可能希望数据库处理分配为标识符的数字序列。 Postgres 10 及更高版本中的现代方法是使标识符 标识列 (请参阅 post by Eisentraut, and post by depesz ). But as you are using 9.4, the legacy approach uses SERIAL
作为伪数据类型。Apparently 你想要 64 位整数,所以使用 BIGSERIAL
/SERIAL8
。
另一个off-topic问题是保留字。在各种 SQL 数据库中保留了超过一千个单词。因此,您很容易在标识符名称与系统关键字和保留字之间发生冲突。在您的情况下,至少 LANGUAGE
是用作列名称的保留字。在我自己的工作中,我使用尾随下划线来避免此类冲突。 SQL 标准明确承诺永远不会在任何 key/reserved 单词上使用尾随下划线。
在下面的代码中,注意我们:
- 对 id 列使用
BIGSERIAL
pseudo-type。
- 在所有标识符上附加下划线
_
。
看起来像:
CREATE TABLE anomaly_
(
id_ BIGSERIAL,
description_ TEXT NOT NULL,
is_translation_anomaly_ BOOLEAN NOT NULL,
PRIMARY KEY (id_)
)
;
CREATE TABLE translation_anomaly_
(
id_ BIGSERIAL,
anomaly_id_ BIGINT NOT NULL,
zone_ VARCHAR(100) NOT NULL,
language_ VARCHAR(100) NOT NULL,
PRIMARY KEY (id_),
FOREIGN KEY (anomaly_id_) REFERENCES anomaly_ (id_),
UNIQUE (zone_, language_, anomaly_id_)
)
;
其他需要考虑的问题包括:命名您的约束,并为您的 zone_
和 language_
列使用 domains or enumerated types。
我正在使用 PostgreSQL 并且有以下 2 个表格:
create table anomaly (
id bigint not null unique,
description text not null,
zone1 varchar(100) not null,
language1 varchar(100) not null,
is_translation_anomaly boolean not null,
primary key(id)
);
create table translation_anomaly (
id bigint not null unique,
zone2 varchar(100) not null,
language2 varchar(100) not null,
primary key(id),
foreign key(id) references anomaly(id)
);
翻译异常 (translation_anomaly
) 是 anomaly
的规范。我想在不使用触发器或存储过程的情况下实现以下两个完整性约束:
- zone1 必须不同于 zone2
- 语言 1 必须不同于语言 2
我已经查看了两个
我正在使用 PostgreSQL 9.4.10.
提前致谢!
在阅读了大量文档并向我的老师寻求帮助后,我得出的结论是 不可能 在不使用触发器的情况下实现上述完整性约束或存储过程。如果您确实需要求助于那些,官方 PostgreSQL 文档非常好:)
我怀疑你的 table 设计不正确 normalized。我不知道你的业务问题的语义,所以我只能猜测。
警告:此处的代码未经测试;语法可能不正确。
标准化
如果 child table 代表本地化中的翻译,那么包括原文在内的所有值都应该出现在 child table 中。
例如,如果您打算在 parent table 中存储英语措辞,而 child table 中存储法语和阿拉伯语,请不要这样做. child table 中应包含所有三种语言(英语、法语、阿拉伯语)。
顺便说一句,每个 table 都需要自己的唯一标识符。所以你的 child table 需要两个字段,一个是它自己的标识符(主键),另一列保存它的 parent (foreign key) 的标识符。
请注意以下代码中我们如何:
- 从第一个 table 中删除了
zone
和language
列。 - 向
translation_anomaly
的 child table 添加了标识符列。
CREATE TABLE anomaly
(
id BIGINT NOT NULL UNIQUE,
description TEXT NOT NULL,
is_translation_anomaly BOOLEAN NOT NULL,
PRIMARY KEY (id)
)
;
CREATE TABLE translation_anomaly
(
id BIGINT NOT NULL UNIQUE,
anomaly_id BIGINT NOT NULL,
zone VARCHAR(100) NOT NULL,
language VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (anomaly_id) REFERENCES anomaly (id)
)
;
Multi-column 唯一约束
- 为
anomaly_id
加zone
列的组合定义唯一索引,以 执行您的第一条规则 。 - 同上
language
加上anomaly_id
以 执行您的第二条规则 。
参见问题,In Postgresql, force unique on combination of two columns。
顺便说一句,无需在标有 PRIMARY KEY
约束的列上声明 NOT NULL
或 UNIQUE
。请参阅文档页面 Constraints。
Adding a primary key will automatically create a unique B-tree index on the column or group of columns listed in the primary key, and will force the column(s) to be marked NOT NULL.
注意以下代码中我们:
- 添加了一对
UNIQUE
约束 - 从标识符列的声明中删除了多余的
NOT NULL
&UNIQUE
。
CREATE TABLE anomaly
(
id BIGINT,
description TEXT NOT NULL,
is_translation_anomaly BOOLEAN NOT NULL,
PRIMARY KEY (id)
)
;
CREATE TABLE translation_anomaly
(
id BIGINT,
anomaly_id BIGINT NOT NULL,
zone VARCHAR(100) NOT NULL,
language VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (anomaly_id) REFERENCES anomaly (id),
UNIQUE (zone, anomaly_id),
UNIQUE (language, anomaly_id)
)
;
如果您的真实规则是每个区域只有一种语言用于任何一种异常,则创建三列 UNIQUE
约束:UNIQUE ( language , zone , anomaly_id )
.
CREATE TABLE anomaly
(
id BIGINT,
description TEXT NOT NULL,
is_translation_anomaly BOOLEAN NOT NULL,
PRIMARY KEY (id)
)
;
CREATE TABLE translation_anomaly
(
id BIGINT,
anomaly_id BIGINT NOT NULL,
zone VARCHAR(100) NOT NULL,
language VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (anomaly_id) REFERENCES anomaly (id),
UNIQUE (zone, language, anomaly_id)
)
;
顺便说一下,您可能希望数据库处理分配为标识符的数字序列。 Postgres 10 及更高版本中的现代方法是使标识符 标识列 (请参阅 post by Eisentraut, and post by depesz ). But as you are using 9.4, the legacy approach uses SERIAL
作为伪数据类型。Apparently 你想要 64 位整数,所以使用 BIGSERIAL
/SERIAL8
。
另一个off-topic问题是保留字。在各种 SQL 数据库中保留了超过一千个单词。因此,您很容易在标识符名称与系统关键字和保留字之间发生冲突。在您的情况下,至少 LANGUAGE
是用作列名称的保留字。在我自己的工作中,我使用尾随下划线来避免此类冲突。 SQL 标准明确承诺永远不会在任何 key/reserved 单词上使用尾随下划线。
在下面的代码中,注意我们:
- 对 id 列使用
BIGSERIAL
pseudo-type。 - 在所有标识符上附加下划线
_
。
看起来像:
CREATE TABLE anomaly_
(
id_ BIGSERIAL,
description_ TEXT NOT NULL,
is_translation_anomaly_ BOOLEAN NOT NULL,
PRIMARY KEY (id_)
)
;
CREATE TABLE translation_anomaly_
(
id_ BIGSERIAL,
anomaly_id_ BIGINT NOT NULL,
zone_ VARCHAR(100) NOT NULL,
language_ VARCHAR(100) NOT NULL,
PRIMARY KEY (id_),
FOREIGN KEY (anomaly_id_) REFERENCES anomaly_ (id_),
UNIQUE (zone_, language_, anomaly_id_)
)
;
其他需要考虑的问题包括:命名您的约束,并为您的 zone_
和 language_
列使用 domains or enumerated types。