zer*_*ble 7 sql postgresql referential-integrity foreign-keys common-table-expression
我有一个示例情况:parenttable有一个名为列的列id,在child表中引用为外键.
删除子行时,如果父项没有被其他子项引用,如何删除它?
Erw*_*ter 10
在PostgreSQL 9.1或更高版本中,您可以使用数据修改CTE通过单个语句执行此操作.这通常不易出错.它最大限度地减少了两个DELETE之间的时间范围,其中竞争条件可能导致并发操作的惊人结果:
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 -- !
);
Run Code Online (Sandbox Code Playgroud)
无论如何,孩子都会被删除.我引用手册:
数据修改语句
WITH只执行一次,并 始终完成,与主查询是否读取其输出的全部(或实际上任何)无关.请注意,这与SELECTin 的规则不同WITH:如上一节所述,SELECT只有在主查询要求其输出时才执行a .
仅在父项没有其他子项时才删除父项.
注意最后一个条件.与人们的预期相反,这是必要的,因为:
子语句
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 -- !
);
Run Code Online (Sandbox Code Playgroud)
这样,一次只有一个事务可以锁定同一个父项.因此,不可能发生多个事务删除同一父项的子项,仍然看到其他子项并保留父项,而所有子项都在之后消失.(仍然允许对非键列进行更新FOR NO KEY UPDATE.)
如果这种情况从未发生过,或者你可以忍受它(几乎从未发生过) - 第一个问题就更便宜了.否则,这是安全的道路.
FOR NO KEY UPDATE与Postgres 9.4一起介绍.手册中的详细信息.在旧版本中使用更强的锁定FOR UPDATE.
| 归档时间: |
|
| 查看次数: |
2355 次 |
| 最近记录: |