【问题标题】:Oracle: delete parent table when child is deleted(Bidirectional Cascade Delete)Oracle:删除子表时删除父表(双向级联删除)
【发布时间】:2015-09-19 00:47:57
【问题描述】:

有一个简单的问题。 所以我有一个名为 PARENT 的表和一个名为 CHILD 的表,假设它们处于父子关系。当另一个表的数据被删除时,是否可以删除其中一个?

我最初的尝试是在删除 PARENT 记录时使用级联删除来删除 CHILD 记录。 同时我在删除 CHILD 的记录时使用触发器删除 PARENT 记录。(条件是在 CHILD 上删除后,触发触发器)

结果是 ORA-04091:表正在变异。 它会造成某种僵局。

父表已删除-->级联删除子表->触发器触发删除父表, 此时,PARENT中删除的记录并没有提交,所以我们处于一个边缘状态。

是否有可能以这种方式关联 Parent 和 Child 表的记录,如果一个被删除,另一个也会被杀死?我们假设这个 CHILD 表是唯一的子表,并且 CHILD 中具有外键的列与 PARENT 中的引用列具有 1 对 1 的关系。

编辑:我偶然发现了用于复合触发器的 oracle 文档,读起来很有趣。如果成功我会更新它。

【问题讨论】:

  • 不能使用双向级联吗?
  • 这甚至是oracle中的东西吗?但这可能是描述问题的最简单方法
  • 只有在父级仍然存在的情况下,您可以让触发器尝试删除吗?
  • Parent 必须存在,因为外键引用它。所以问题是当父级被删除时,它会激活级联删除,然后触发子级触发。数据库必须在提交之前完成所有这些连锁反应。在它提交之前,父记录在技术上不会被删除。
  • 我认为它应该与相互外键约束一起使用。将它们定义为“延迟”,否则您无法插入任何新数据

标签: database oracle parent-child cascading-deletes


【解决方案1】:

这种级联通常没有意义。 创建孩子时,父母必须存在。所以这是常识,不应该仅仅因为没有孩子就删除父母。

如果你只使用一对一的关系,当你需要删除子记录时,你只需要改变你的程序逻辑,只删除父记录。

但是,如果你真的需要这个东西。我称之为 LONELY_PARENT_CLEANUP 程序。现在,当我们有了名称和含义时,我们只需要执行它的方法。 由于我们不能从触发器运行它(变异表!),我们应该进行清理工作。我们可以从触发器或按某个时间表启动这项工作。在清理父级之前可能会稍有延迟。

假设我们有 TEST_PARENT 表,列 (id)TEST_CHILD 表,列 (id, parent_id)。我们使用 DELETE CASCADE 创建了 FK。
创建删除父进程的过程。我建议我们尽快删除父级。

CREATE OR REPLACE PROCEDURE DELETE_LONELY_PARENT (p_id IN NUMBER) AS 
BEGIN
  DELETE FROM test_parent WHERE id = p_id;
  COMMIT;
END DELETE_LONELY_PARENT;

现在我们需要TEST_CHILD 表上的触发器:

CREATE OR REPLACE TRIGGER DELETE#PARENT#TRG 
BEFORE DELETE ON TEST_CHILD FOR EACH ROW
DECLARE
  jobId NUMBER;
BEGIN
  dbms_job.submit(
    job => jobId, 
    what => 'BEGIN DELETE_LONELY_PARENT('|| :old.parent_id ||'); END;'
  );
END;

现在当您删除子记录时,特殊作业将开始删除父记录。由于事务不同,因此不再存在变异表问题。

【讨论】:

  • 那么当触发器触发程序时,它不是要等到程序完成删除父节点后再提交吗?
  • @ncgzeal 没错,提交随它进行,并删除由调度程序在其他会话中单独启动的例程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-16
  • 2021-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多