我有一个例子情况: parent
表中有一个名为列id
,在引用child
表的外键。
当删除子行,如何删除父以及如果它没有通过任何其他子引用?
我有一个例子情况: parent
表中有一个名为列id
,在引用child
表的外键。
当删除子行,如何删除父以及如果它没有通过任何其他子引用?
在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
正在执行一次, 始终把完成 ,独立的是否主要查询读取所有(或任何)的输出。 注意,这是从规则为不同SELECT
在WITH
:如前一节中所述,执行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
来代替。
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;