Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 删除父项(如果为');s未被任何其他子级引用_Sql_Postgresql_Foreign Keys_Common Table Expression_Referential Integrity - Fatal编程技术网

Sql 删除父项(如果为');s未被任何其他子级引用

Sql 删除父项(如果为');s未被任何其他子级引用,sql,postgresql,foreign-keys,common-table-expression,referential-integrity,Sql,Postgresql,Foreign Keys,Common Table Expression,Referential Integrity,我有一个示例情况:parent表有一个名为id的列,在child表中作为外键引用 删除子行时,如果父行未被任何其他子行引用,如何删除父行 delete from child where parent_id = 1 在子项中删除后,在父项中执行: delete from parent where id = 1 and not exists ( select 1 from child where parent_id = 1 ) 不存在条件将确保只有在子项中

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

删除子行时,如果父行未被任何其他子行引用,如何删除父行

delete from child
where parent_id = 1
在子项中删除后,在父项中执行:

delete from parent
where
    id = 1
    and not exists (
        select 1 from child where parent_id = 1
    )
不存在
条件将确保只有在子项中不存在时才会将其删除。您可以在事务中包装这两个删除命令:

begin;
first_delete;
second_delete;
commit;

在PostgreSQL9.1或更高版本中,您可以通过使用。这通常不太容易出错。它最小化了两次删除之间的时间间隔,在这两次删除之间,竞争条件可能导致并发操作的意外结果:

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   -- !
   );
这样,一次只有一个事务可以锁定同一父事务。因此,不可能发生多个事务删除同一父级的子级,仍然看到其他子级并保留父级,而所有子级随后都消失的情况。(对于无键更新,仍允许使用
对非键列进行更新

如果这种情况从未发生过,或者你可以忍受它(几乎从未发生过),那么第一个查询会更便宜。否则,这就是安全路径


Postgres 9.4引入了无密钥更新的
。在较旧的版本中,将更强的锁
用于更新

它最小化了两次删除之间的时间范围,在这两次删除之间,竞争条件可能会导致意外的结果,如果两次删除都包装在一个事务中,可能会发生并发操作?@ClodoaldoNeto:是的,可能会。沿途都会获得锁。并发事务可以在两个
删除
之间插入一个子项。更新:如果先提交
INSERT
,则父级上的
DELETE
将不会通过。如果
DELETE
先提交,我认为
INSERT
将以外键冲突结束并回滚。不太可能,因为时间范围很小,但有可能。事务失败是否会产生意外的结果?取决于用例和并发负载。我从来没有见过它失败过(除非我为了一个测试用例而激发它)。@Felix:因为父母只有在没有孩子的时候才会被删除,所以在这个场景中,级联FK永远不会触发。但这两种方式都应该奏效。如果仍然不清楚,请开始一个新问题。您始终可以引用此文件作为上下文。
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   -- !
   );