如何CASCADE从子表删除到父表?

时间:2014-03-18 05:34:56

标签: sql postgresql database-design foreign-keys cascade

我准备了a fiddle which demonstrates the problem

CREATE TABLE parent (
   parent_id integer primary key
);

CREATE TABLE child (
   child_name TEXT primary key,
   parent_id integer REFERENCES parent (parent_id) ON DELETE CASCADE
);

INSERT INTO parent VALUES (1);
INSERT INTO child VALUES ('michael',1), ('vanessa', 1);

我希望删除子记录时删除CASCADE到父记录的方法 例如:

DELETE FROM child WHERE child_name='michael';

这应该级联到父表并删除记录。

3 个答案:

答案 0 :(得分:9)

外键仅在另一个方向上工作:级联从父级删除到子级,因此当删除父级(引用的)记录时,也会删除任何子级(引用)记录。

如果它是1:1关系,您可以创建双向外键关系,其中一边是DEFERRABLE INITIALLY DEFERRED,并且两边都是级联。

否则,您将需要子表上的ON DELETE ... FOR EACH ROW触发器,如果​​没有剩余子项,则会删除父行。同时INSERT s可能会出现竞争条件;您需要SELECT ... FOR UPDATE父记录,然后检查其他子记录。对插入的外键检查会对引用的(父)记录执行FOR SHARE锁定,以防止出现任何争用情况。

答案 1 :(得分:2)

你似乎想要杀死整个家庭,而不考虑剩下的孩子。运行此而不是DELETE FROM child ...

DELETE FROM parent p
USING  child c
WHERE  p.parent_id = c.parent_id
AND    c.child_name='michael';

然后一切都适合您当前的设计。如果您坚持原始DELETE声明,则需要规则或触发器。但那会很混乱。

DELETE statement in the manual.

答案 2 :(得分:0)

从你的问题和sql小提琴,你确定要删除父和所有孩子,如果一个孩子被删除?如果是这样,那么使用:

CREATE FUNCTION infanticide () RETURNS trigger AS $$
BEGIN
  DELETE FROM parent WHERE parent_id = OLD.parent_id;
  RETURN NULL;
END; $$ LANGUAGE plpgsql;

CREATE TRIGGER dead_kids BEFORE DELETE ON TABLE child
  FOR EACH ROW EXECUTE infanticide();

您必须RETURN NULL触发BEFORE DELETE:您希望删除失败。原因是您删除了父级,并且该删除将级联到子表(在父表上使用适当的设置)。如果您尝试在同一语句中删除更多子项,那么您正在尝试使系统处理不存在的数据并抛弃已经消失的父项。