3

我有以下表格:

CREATE TABLE mail (
    id serial,
    parent_mail_id integer,
    ...

    PRIMARY KEY (id),
    FOREIGN KEY (parent_mail_id) REFERENCES mail(id),
    ...
);

CREATE TABLE incoming (
    from_contact_id integer NOT NULL REFERENCES contact(id),
    ...
    PRIMARY KEY (id),
    ---> FOREIGN KEY (parent_mail_id) REFERENCES mail(id), <---
    ...
) INHERITS(mail);

CREATE TABLE outgoing (
    from_user_id integer NOT NULL REFERENCES "user"(id),
    ...  
    PRIMARY KEY (id),
    --> FOREIGN KEY (parent_mail_id) REFERENCES mail(id), <--
    ...
) INHERITS(mail);

incoming并再次outgoing继承mail并定义它们的外键(和主键),因为它们不是自动继承的。

问题是:

如果我要插入一封incoming邮件,则无法从outgoing表中引用它,因为外键仅适用于超级表 ( mails)。

有解决方法吗?

4

2 回答 2

4

PostgreSQL 9.3 文档

继承特性的一个严重限制是索引(包括唯一约束)和外键约束仅适用于单个表,而不适用于它们的继承子表。在外键约束的引用侧和被引用侧都是如此。因此,在上面的例子中:

如果我们将 city.name 声明为 UNIQUE 或 PRIMARY KEY,这不会阻止 capitals 表中的行名称与城市中的行重复。默认情况下,这些重复的行会出现在来自城市的查询中。事实上,默认情况下,大写字母根本没有唯一约束,因此可以包含多个具有相同名称的行。您可以为首都添加独特的约束,但这不会阻止与城市相比的重复。

同样,如果我们指定 city.name REFERENCES 某个其他表,则此约束不会自动传播到大写字母。在这种情况下,您可以通过手动将相同的 REFERENCES 约束添加到大写字母来解决它。

指定另一个表的列 REFERENCES city(name) 将允许另一个表包含城市名称,但不能包含大写名称。这种情况没有好的解决方法。

这些缺陷可能会在将来的某个版本中得到修复,但 与此同时,在确定继承是否对您的应用程序有用时需要相当小心。

并不是真正的解决方法,所以也许让邮件成为一个非继承表,然后为它们各自的额外列分开传入_列和传出_列,邮件 ID 作为它们的主键和外键。例如,您可以创建一个作为邮件 INNER JOIN 传出_columns 的传出视图。

于 2013-08-02T19:08:50.067 回答
3

您可以使用约束触发器

CREATE OR REPLACE FUNCTION mail_ref_trigger()
  RETURNS trigger AS
$BODY$
DECLARE
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM mail WHERE id = NEW.parent_mail_id
        ) THEN
        RAISE foreign_key_violation USING MESSAGE = FORMAT('Referenced mail id not found, mail_id:%s', NEW.parent_mail_id);
    END IF;
    RETURN NEW;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE CONSTRAINT TRIGGER mail_fkey_trigger
    AFTER UPDATE OR INSERT ON incoming
    DEFERRABLE
    FOR EACH ROW EXECUTE PROCEDURE mail_ref_trigger();
于 2015-05-06T19:03:37.370 回答