如何在视图上写删除规则?

时间:2011-04-04 05:35:45

标签: postgresql

我正在尝试在视图上编写规则以从组件表中删除元组,但到目前为止只能从其中一个中删除数据。我已经将postgres与基本视图使用了一段时间,但我对视图规则没有任何经验。

我写了一个愚蠢的小测试用例来弄清楚/显示我的问题。在这个例子中,每个子元组只有一个父元组(我的实际模式当然不是这样的。)

组件表:

CREATE TABLE parent(
   id serial PRIMARY KEY,
   p_data integer NOT NULL UNIQUE
);
CREATE TABLE child(
   id serial PRIMARY KEY,
   parent_id integer NOT NULL UNIQUE REFERENCES parent(id),
   c_data integer NOT NULL
);

查看:

CREATE TABLE child_view(
   id integer,
   p_data integer,
   c_data integer
);
CREATE RULE "_RETURN" AS ON SELECT TO child_view DO INSTEAD
   SELECT child.id, p_data, c_data 
      FROM parent JOIN child ON (parent_id=parent.id);

问题删除规则

CREATE RULE child_delete AS ON DELETE TO child_view DO INSTEAD(
   DELETE FROM child WHERE id=OLD.id;
   DELETE FROM parent WHERE p_data=OLD.p_data;
);

上述规则的目的是从组件表中删除视图中引用的元组。 WHERE p_data=OLD.p_data对我来说似乎很奇怪,但我不知道如何在父表中引用所需的元组。

以下是我尝试使用上述规则时会发生的情况:

>SELECT * FROM child_view;
 id | p_data | c_data 
----+--------+--------
  1 |      1 |     10
  2 |      2 |     11
  3 |      3 |     12
(3 rows)

>DELETE FROM child_view WHERE id=3;
DELETE 0

>SELECT * FROM child_view;
 id | p_data | c_data 
----+--------+--------
  1 |      1 |     10
  2 |      2 |     11
(2 rows)

但是查看父表,删除的第二部分不起作用(id = 3“should”已被删除):

>SELECT * FROM parent;
 id | p_data 
----+--------
  1 |      1
  2 |      2
  3 |      3
(3 rows)

如何编写删除规则以删除子元组和父元组?

这是使用postgres v9。

感谢任何帮助。除了postgres文档之外,还有指向任何涵盖视图规则的材料的指示(除非我明显错过了某些内容)也将受到赞赏。感谢。

编辑:正如jmz所指出的,使用级联删除比这里的规则更容易,但这种方法对我的实际模式不起作用。

2 个答案:

答案 0 :(得分:5)

您在规则问题中看到的是规则系统不会原子地处理数据。无论DO INSTEAD规则中两个语句的顺序如何,都会执行第一次删除。永远不会执行第二个语句,因为OLD.id引用的行已从视图中删除。您可以使用LEFT JOIN,但由于示例表设计(它可能适用于您的实际数据库架构),这对您没有帮助。

我认为,根本问题在于你正在对规则系统进行处理。

您最好的选择是使用外键和ON DELETE CASCADE而不是规则。使用它们,您的示例模式也可以工作:您只需要删除父表就可以删除所有子项。

答案 1 :(得分:0)

你想做的事情会很好。但你左转:

CREATE TABLE child_view(
   id integer,
   p_data integer,
   c_data integer
);
CREATE RULE "_RETURN" AS ON SELECT TO child_view DO INSTEAD
   SELECT child.id, p_data, c_data 
      FROM parent JOIN child ON (parent_id=parent.id);

你想要一个真实的生活视图,而不是一张桌子。这就是为什么删除不起作用。

CREATE VIEW child_view AS SELECT
    child.id,
    p_data,
    c_data
    FROM parent
        JOIN child ON (parent_id=parent.id)
;

将顶部替换为底部,它将完美地工作(当我测试它时它会这样做)。删除不起作用的原因是它试图从TABLE子视图中删除id,这当然是空的!它不执行'select do instead'规则,因此它正在处理真实的子表视图。人们可能会使用规则大便,但如果他们看不到这么明显的错误,我想知道他们知道多少?

我已成功使用规则来定义接口以强制执行业务规则。他们可以通过触发器无法实现优雅的解决方案。

注意:我只建议这样做一个接口的可写视图。你可以做一些聪明的事情,比如检查表间的约束 - 你可能要求它。真的应该与触发器一起使用。

编辑:每个请求的脚本

-- set this as you may have had an error if you running 
-- from a script and not noticed it with all the NOTICES
\set ON_ERROR_STOP

drop table if exists parent cascade;
drop table if exists child cascade;

CREATE TABLE parent(
        id serial PRIMARY KEY,
        p_data integer NOT NULL UNIQUE
);

CREATE TABLE child(
        id serial PRIMARY KEY,
        parent_id integer NOT NULL UNIQUE REFERENCES parent(id),
        c_data integer NOT NULL
);

        CREATE VIEW child_view AS SELECT
                child.id,
                p_data,
                c_data
                FROM parent 
                        JOIN child ON (parent_id=parent.id)
        ;

CREATE RULE child_delete AS ON DELETE TO child_view DO INSTEAD(
        DELETE FROM child WHERE id=OLD.id;
        DELETE FROM parent WHERE p_data=OLD.p_data;
);

insert into parent (p_data) values (1), (2), (3);
insert into child (parent_id, c_data) values (1, 1), (2, 2), (3, 3);

select * from child_view;

 id | p_data | c_data 
----+--------+--------
  1 |      1 |      1
  2 |      2 |      2
  3 |      3 |      3
(3 rows)

delete from child_view where id=3;
DELETE 0

select * from child_view;
 id | p_data | c_data 
----+--------+--------
  1 |      1 |      1
  2 |      2 |      2
(2 rows)