删除父如果它没有通过任何其他子引用删除父如果它没有通过任何其他子引用(Delete parent i

2019-05-12 03:13发布

我有一个例子情况: parent表中有一个名为列id ,在引用child表的外键。

当删除子行,如何删除父以及如果它没有通过任何其他子引用?

Answer 1:

在PostgreSQL 9.1或更高版本 ,可以使用一个语句做到这一点修改数据的CTE 。 这通常是不容易出错。 它最大限度地减少两个删除其中一个比赛条件可能会导致并发操作令人惊讶的结果之间的时间框架:

WITH del_child AS (
    DELETE FROM child
    WHERE  child_id = 1
    RETURNING parent_id, child_id
    )
DELETE FROM parent p
USING  del_child x
WHERE  p.parent_id = x.parent_id
AND    NOT EXISTS (
   SELECT 1
   FROM   child c
   WHERE  c.parent_id = x.parent_id
   AND    c.child_id <> x.child_id   -- !
   );

SQL小提琴。

孩子在任何情况下删除。 我引述手册 :

在修改数据的语句WITH正在执行一次, 始终把完成 ,独立的是否主要查询读取所有(或任何)的输出。 注意,这是从规则为不同SELECTWITH :如前一节中所述,执行SELECT仅只要该主查询要求它的输出进行。

如果没有其他子女的父或母只能删除。
注意最后一个条件。 相反的是人们所预料的,这是必要的,因为:

在子报表WITH彼此以及与主查询并发执行。 因此,在使用数据修改语句时WITH ,在指定的更新实际发生的顺序是不可预知的。 所有语句都具有相同的快照(参见第13章)执行,因此它们无法“看到”彼此的目标表的影响。

大胆重点煤矿。
我使用的列名parent_id代替非描述性的id

消除竞争条件

为了消除可能的竞争条件,我完全上面提到的, 首先锁定父行。 当然, 所有类似的操作必须遵循同样的程序,使其工作。

WITH lock_parent AS (
   SELECT p.parent_id, c.child_id
   FROM   child  c
   JOIN   parent p ON p.parent_id = c.parent_id
   WHERE  c.child_id = 12              -- provide child_id here once
   FOR    NO KEY UPDATE                -- locks parent row.
   )
 , del_child AS (
   DELETE FROM child c
   USING  lock_parent l
   WHERE  c.child_id = l.child_id
   )
DELETE FROM parent p
USING  lock_parent l
WHERE  p.parent_id = l.parent_id
AND    NOT EXISTS (
   SELECT 1
   FROM   child c
   WHERE  c.parent_id = l.parent_id
   AND    c.child_id <> l.child_id   -- !
   );

这一次的方式只有一个事务可以锁定相同的父。 因此,它不能发生多次交易删除同一母公司的孩子,还是看其他的孩子和饶父,而所有的孩子都走了之后。 (非键列的更新仍然是允许用FOR NO KEY UPDATE )。

如果这样的情况不会发生,或者你可以住在一起(很少)发生 - 第一个查询便宜。 否则,这是安全的路径。

FOR NO KEY UPDATE用的Postgres 9.4推出。 详细的说明书中无。 在旧版本中使用更强的锁定FOR UPDATE来代替。



Answer 2:

delete from child
where parent_id = 1

在孩子删除后做父:

delete from parent
where
    id = 1
    and not exists (
        select 1 from child where parent_id = 1
    )

not exists条件将确保,如果它不符合儿童存在,才会被删除。 你可以用这两个在一个事务中删除命令:

begin;
first_delete;
second_delete;
commit;


文章来源: Delete parent if it's not referenced by any other child