【问题标题】:PostgreSQL foreign key not existing, issue of inheritance?PostgreSQL外键不存在,继承问题?
【发布时间】:2011-06-23 21:09:35
【问题描述】:

我在数据库中遇到外键问题,可能与继承有关?
下面是基本设置:

-- table address
CREATE TABLE address
(
  pk_address serial NOT NULL,
  fk_gadmid_0 integer NOT NULL, -- this table already exists, no problem here
  street character varying(100),
  zip character varying(10),
  city character varying(50),
  public boolean,
  CONSTRAINT address_primarykey PRIMARY KEY (pk_address),
  CONSTRAINT gadmid_0_primarykey FOREIGN KEY (fk_gadmid_0)
      REFERENCES adm0 (gadmid_0) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE
);
ALTER TABLE address OWNER TO postgres;  

-- table stakeholder (parent)
CREATE TABLE stakeholder
(
    pk_stakeholder integer DEFAULT nextval('common_stakeholder_seq') NOT NULL,
    fk_stakeholder_type integer NOT NULL, -- this table also exists, no problem here
    name character varying(255) NOT NULL,
    CONSTRAINT stakeholder_primarykey PRIMARY KEY (pk_stakeholder),
    CONSTRAINT stakeholder_fk_stakeholder_type FOREIGN KEY (fk_stakeholder_type)
        REFERENCES stakeholder_type (pk_stakeholder_type) MATCH SIMPLE
        ON UPDATE CASCADE ON DELETE NO ACTION
)
WITH (
    OIDS=FALSE
);
ALTER TABLE stakeholder OWNER TO postgres;  

-- table individual (child of stakeholder)
CREATE TABLE individual
(
    firstname character varying(50),
    fk_title integer, -- this table also exists, no problem here
    email1 character varying (100),
    email2 character varying (100),
    phone1 character varying (50),
    phone2 character varying (50),
    CONSTRAINT individual_primarykey PRIMARY KEY (pk_stakeholder),
    CONSTRAINT title_foreignkey FOREIGN KEY (fk_title)
        REFERENCES title (pk_title) MATCH SIMPLE
        ON UPDATE CASCADE ON DELETE CASCADE
) INHERITS (stakeholder)
WITH (
    OIDS=FALSE
);
ALTER TABLE individual OWNER TO postgres;  

-- link between stakeholder and address
CREATE TABLE l_stakeholder_address
(
    pk_l_stakeholder_address serial NOT NULL,
    fk_stakeholder integer NOT NULL REFERENCES stakeholder,
    fk_address integer NOT NULL REFERENCES address,
    CONSTRAINT l_stakeholder_address_primarykey PRIMARY KEY (pk_l_stakeholder_address),
    CONSTRAINT l_stakeholder_address_fk_stakeholder FOREIGN KEY (fk_stakeholder)
        REFERENCES stakeholder (pk_stakeholder) MATCH SIMPLE
        ON UPDATE CASCADE ON DELETE NO ACTION,
    CONSTRAINT l_stakeholder_address_fk_address FOREIGN KEY (fk_address)
        REFERENCES address (pk_address) MATCH SIMPLE
        ON UPDATE CASCADE ON DELETE NO ACTION
)
WITH (
    OIDS=FALSE
);
ALTER TABLE l_stakeholder_address OWNER TO postgres;

到目前为止,没问题。然后我尝试添加一些值:

INSERT INTO individual (pk_stakeholder, fk_stakeholder_type, name, firstname, fk_title, email1, email2, phone1, phone2) 
  VALUES (1, 8, 'Lastname', 'Firstname', 1, 'me@you.com', '', '', '');
INSERT INTO address (pk_address, fk_gadmid_0, street, zip, city, public)  
  VALUES (1, 126, 'Address', '', 'City', FALSE);
INSERT INTO l_stakeholder_address (pk_l_stakeholder_address, fk_stakeholder, fk_address)  
  VALUES (DEFAULT, 1, 1);

最后我遇到了一个错误(SQL 状态 23503),说密钥 (fk_stakeholder)=(1) 在表“利益相关者”中不存在。
前 2 个插入很好,我可以在数据库中看到它们:

stakeholder:
pk_stakeholder | ...
----------------------
1              | ...

address:
pk_address | ...
--------------------
1          | ...

我做错了什么?我必须承认我对 PostgreSQL(使用 8.4)相当陌生,但我什至不确定这是否是 PG 的问题,也许我只是缺乏一些基本的数据库设计理解......
无论哪种方式,到目前为止,我已经尝试了几乎所有我能想到的方法,我还尝试像 PostgreSQL : Transaction and foreign key problem 那样使 FK 可延迟,但不知何故,这也不起作用。

【问题讨论】:

    标签: postgresql inheritance foreign-keys


    【解决方案1】:

    您的分析完全正确:这是因为继承。检查外键时,不考虑子表。

    一般来说,继承和外键在 PostgreSQL 中不能很好地混合。一个主要问题是您不能跨表设置唯一约束。

    Reference

    【讨论】:

    • 感谢您的快速回答。我曾经的理解是,子表在这里无关紧要,因为它们没有 PK(继承的除外),并且 FK 引用了父表(正确存储了 PK)。但显然我错了(虽然我不知道为什么)。那么有没有办法做我想做的事?也许使用触发器检查链接表中的fk_stakeholder是否存在于stakeholder的PK中?
    • 这里不关心主键。外键 fk_stakeholder 指向表涉众,但那里没有匹配的行。不考虑个人表中的行。
    • 说实话,我还是不明白。外键指向stakeholder.pk_stakeholder,它存在是因为它被其子表插入。我想这就是问题所在 - 但仍然存在这一行。正如上面的其他解决方案中的利益相关者 pk.pk_stakeholder 一样。我想我在这个问题上缺乏一些基本的理解。但没关系,我不想占用你更多的时间,而且它似乎可以使用 Tometzky 提出的解决方案。但是非常感谢您的帮助!
    • 运行SELECT * FROM ONLY stakeholder。这就是 FK 检查的作用。那里没有行。
    • 解决继承造成的复杂性的一种解决方案,尤其是继承的 ID 属性被共享的期望:创建一个触发器,每个子表都会触发以在所有子表中强制执行 RI。这类似于单表 RI 检查,但速度不快。它确实实现了大多数开发人员在发现继承并在假定净收益的情况下开始使用它时所期望的模式。
    【解决方案2】:

    您可以使用附加表 individual_pks (individual_pk integer primary key) 来解决它,其中包含来自父项和子项的所有主键,这将使用触发器进行维护(非常简单 - 插入时插入 individual_pks,删除时从中删除,更新它更新时,如果它改变了individual_pk)。

    然后你将外键指向这个额外的表而不是一个子表。会有一些小的性能影响,但仅限于添加/删除行时。

    或者忘记继承并使用旧方法 - 只需一个包含一些可为空列的表。

    【讨论】:

    • 非常感谢您的回答,我设法以这种方式解决了我的问题。但是,我宁愿使用“stakeholder_pk”而不是“individual_pks”,因为涉众是父表。所以 'stakeholder_pk' 收集了 'stakeholder' 及其子项的所有主键。谢谢!
    猜你喜欢
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 2012-06-09
    • 2011-12-29
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多