database - Postgres 中的循环删除级联

标签 database postgresql cascade postgresql-12

(作为背景,我使用的是 Postgres 12.4)
我不清楚为什么删除在两个表之间存在循环 FK 并且两个 FK 都设置为 ON DELETE CASCADE 时起作用。

CREATE TABLE a (id bigint PRIMARY KEY);
CREATE TABLE b (id bigint PRIMARY KEY, aid bigint references a(id) on delete cascade);
ALTER TABLE  a ADD COLUMN bid int REFERENCES b(id) ON DELETE CASCADE ;

insert into a(id) values (5);
insert into b(id, aid) values (10,5);
update a set bid = 10 where id=5;

DELETE from a where id=5;

我正在考虑的方式是,当您删除表 'a' 中 PK id = 5 的行时,postgres 查看具有引用 a(id) 的引用约束的表,它找到 b,它尝试删除该行在 id = 10 的表 b 中,然后它必须查看引用 b(id) 的表,因此它返回到 a,然后它应该以无限循环结束。
但情况似乎并非如此。删除完成且没有错误。
正如一些消息来源在网上所说,情况也并非如此,您无法创建圆形约束。约束创建成功,它们都不是可延迟的。
所以我的问题是 - 为什么 postgres 完成这个循环级联,即使两个约束都没有设置为可延迟,如果它能够这样做,那么即使有一个可延迟选项又有什么意义呢?

最佳答案

外键约束作为系统触发器实现。
对于 ON DELETE CASCADE ,此触发器将运行如下查询:

/* ----------
 * The query string built is
 *  DELETE FROM [ONLY] <fktable> WHERE $1 = fkatt1 [AND ...]
 * The type id's for the $ parameters are those of the
 * corresponding PK attributes.
 * ----------
 */
查询运行的是一个新的数据库快照,因此它看不到先前 RI 触发器删除的行:
/*
 * In READ COMMITTED mode, we just need to use an up-to-date regular
 * snapshot, and we will see all rows that could be interesting. But in
 * transaction-snapshot mode, we can't change the transaction snapshot. If
 * the caller passes detectNewRows == false then it's okay to do the query
 * with the transaction snapshot; otherwise we use a current snapshot, and
 * tell the executor to error out if it finds any rows under the current
 * snapshot that wouldn't be visible per the transaction snapshot.  Note
 * that SPI_execute_snapshot will register the snapshots, so we don't need
 * to bother here.
 */
这可确保 RI 触发器不会再次尝试删除同一行,从而破坏循环。
(所有引文均来自 src/backend/utils/adt/ri_triggers.c 。)

关于database - Postgres 中的循环删除级联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69454969/

相关文章:

sql - 权限表的性能问题

"FROM"不同运算符用法之间的MySQL性能差异

database - 每 5 分钟更新一次全表

python - SqlAlchemy sql.text 提升限制子句

java - 如何将CellStyle对象存储到数据库中?

postgresql - 索引已创建,但我得到了序列扫描

java - Hibernate:如果子项以多对一方式链接到父项,如何让 Hibernate 在删除父项时从子表中删除记录?

sql - 在两列上使用 COUNT 和 GROUP BY 进行非常慢的 SQL 查询

hibernate - 级联删除操作在 hibernate 中未按预期工作

SQL ON DELETE CASCADE,删除是通过哪种方式发生的?