PostgreSQL删除失败,继承表上有ON DELETE规则

时间:2011-09-28 08:03:16

标签: sql postgresql inheritance rules

在我的PostgreSQL 9.1数据库中,我定义了每当删除父表行时从子表中删除行的RULE。这一切都运行正常,直到我引入继承。如果来自另一个表的父(引用)表INHERITS并且我从基表中删除然后DELETE成功,但RULE似乎根本没有触发 - 引用的行不会被删除。如果我尝试从派生表中删除,我会收到错误:

update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"

父表中没有其他行会违反外键:它被被删除的行引用!我该如何解决这个问题?

以下脚本重现了该问题:

-- Schema

CREATE TABLE base
(
  id serial NOT NULL,
  name character varying(100),
  CONSTRAINT pk_base PRIMARY KEY (id)
);

CREATE TABLE referenced
(
  id serial NOT NULL,
  value character varying(100),
  CONSTRAINT pk_referenced PRIMARY KEY (id)
);

CREATE TABLE derived
(
  referenced_id integer,
  CONSTRAINT pk_derived PRIMARY KEY (id),
  CONSTRAINT fk_derived_referenced FOREIGN KEY (referenced_id) REFERENCES referenced (id)
)
INHERITS (base);

-- The rule

CREATE OR REPLACE RULE rl_derived_delete_referenced
AS ON DELETE TO derived DO ALSO
DELETE FROM referenced r WHERE r.id = old.referenced_id;

-- Some test data

INSERT INTO referenced (id, value)
VALUES (1, 'referenced 1');

INSERT INTO derived (id, name, referenced_id)
VALUES (2, 'derived 2', 1);

-- Delete from base - deletes the "base" and "derived" rows, but not "referenced"
--DELETE FROM base
--WHERE id = 2;

-- Delete from derived - fails with:
-- update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"
DELETE FROM derived
WHERE id = 2

2 个答案:

答案 0 :(得分:1)

正如我在评论中所说,这似乎是一种不寻常的做事方式。但是你可以使用延迟约束。

CREATE TABLE derived
(
  referenced_id integer,
  CONSTRAINT pk_derived PRIMARY KEY (id),
  CONSTRAINT fk_derived_referenced FOREIGN KEY (referenced_id) 
    REFERENCES referenced (id) DEFERRABLE INITIALLY DEFERRED
)
INHERITS (base);

PostgreSQL文档,Rules vs. Triggers,说

  

也可以实现使用触发器可以完成的许多事情   使用PostgreSQL规则系统。其中一件事情不可能   规则实施的是一些约束,尤其是外来的约束   密钥。

但我不清楚这个特定的限制是你遇到的。

答案 1 :(得分:0)

此外,您还需要检查其他记录是否仍在引用要删除的行。我添加了一个测试派生记录#3,它指向相同的#1参考记录。

-- The rule    
CREATE OR REPLACE RULE rl_derived_delete_referenced
AS ON DELETE TO tmp.derived DO ALSO (
    DELETE FROM tmp.referenced re_del
    WHERE re_del.id = OLD.referenced_id
    AND NOT EXISTS ( SELECT * FROM tmp.derived other
        WHERE other.referenced_id = re_del.id
        AND other.id <> OLD.id )
        ;
    );

-- Some test data

INSERT INTO tmp.referenced (id, value)
VALUES (1, 'referenced 1');

-- EXPLAIN ANALYZE
INSERT INTO tmp.derived (id, name, referenced_id)
VALUES (2, 'derived 2', 1); 

INSERT INTO tmp.derived (id, name, referenced_id)
VALUES (3, 'derived 3', 1);

-- Delete from base - deletes the "base" and "derived" rows, but not "referenced"
--DELETE FROM base
--WHERE id = 2;

-- Delete from derived - fails with:
-- update or delete on table "referenced" violates foreign key constraint "fk_derived_referenced" on table "derived"

EXPLAIN ANALYZE
DELETE FROM tmp.derived
WHERE id = 2
    ;

SELECT * FROM tmp.base;
SELECT * FROM tmp.derived;
SELECT * FROM tmp.referenced;