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 的规范。我想在不使用触发器或存储过程的情况下实现以下两个完整性约束:

我已经查看了两个 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 中删除了 zonelanguage 列。
  • 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_idzone 列的组合定义唯一索引,以 执行您的第一条规则
  • 同上 language 加上 anomaly_id 执行您的第二条规则

参见问题,In Postgresql, force unique on combination of two columns

顺便说一句,无需在标有 PRIMARY KEY 约束的列上声明 NOT NULLUNIQUE。请参阅文档页面 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