【问题标题】:Prevent recursive trigger in PostgreSQL防止 PostgreSQL 中的递归触发器
【发布时间】:2010-10-17 01:14:34
【问题描述】:

如何防止触发器递归执行?假设我想在会计科目表上构建一个“可树”的描述。所以我所做的是当插入/更新一条新记录时,我会更新父记录的down_qty,所以这会递归地触发更新触发器。

现在,我的代码没问题 - 我把它放在 UPDATE 触发器的第一行:

-- prevents recursive trigger
if new.track_recursive_trigger <> old.track_recursive_trigger then
    return new;
end if;

当我需要更新父记录的数量时,这是我的触发器中的示例代码:

update account_category set 
    track_recursive_trigger = track_recursive_trigger + 1, -- i put this line to prevent recursive trigger
    down_qty = down_qty - (old.down_qty + 1)
where account_category_id = m_parent_account;

我在想 PostgreSQL 中是否有一种方法可以在不引入新字段的情况下检测递归触发器,类似于 MSSQL 的 trigger_nestlevel

[编辑]

我在树内循环,我需要将每个account_categorydown_qty 冒泡回到它的根。例如,我插入一个新的帐户类别,它需要增加其父account_categorydown_qty,同样当我更改帐户类别的父account_category时,我需要减少account_categorydown_qty' s 以前的父母account_category。虽然我认为可以,但我不会让 PostgreSQL 执行递归触发器。我之前使用 MSSQL,触发递归深度级别仅限于 16 级别。

【问题讨论】:

  • 我认为你应该更详细地解释一下,因为据我所知,这里没有递归调用的危险,除非你有:记录是它自己的父级,或者循环你的树(那不是树)。

标签: postgresql recursion


【解决方案1】:

在 pg 中,由你来跟踪触发器递归。

如果触发函数执行 SQL 命令然后这些命令可能 再次触发火灾。这被称为 级联触发器。没有直接的 级联数限制 水平。级联是可能的 导致递归调用 相同的触发器;例如,一个 INSERT 触发器可能会执行一个命令 在 同一张表,导致 INSERT 触发器 再次被解雇。这是触发器 避免程序员的责任 在这种情况下无限递归。

https://www.postgresql.org/docs/13/trigger-definition.html

【讨论】:

  • 抱歉,但必须投反对票,因为问题询问了 如何 以防止递归。这个答案表明用户需要跟踪触发器递归,问题已经承认,并且是被问到如何解决的问题。
【解决方案2】:

为避免无限递归,请参阅 my answer here。正如其他人评论的那样,如果您的数据结构是一棵真正的树(根将没有父级)并且递归将总是在根处停止。对于只有一个父指针节点,无限递归的唯一方法是存在循环。 (我链接中的方法将访问任何节点最多一次)

【讨论】:

    【解决方案3】:

    这就是我在 PostgreSQL 9.2 中所做的,尽管我必须承认我没有发现这种方法的文档。有一个函数pg_trigger_depth()documented here,我用它来区分触发器中的原始调用和嵌套调用。

    CREATE TRIGGER trg_taxonomic_positions
    AFTER INSERT OR UPDATE OF taxonomic_position
    ON taxon_concepts
    FOR EACH ROW
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE trg_taxonomic_positions()
    

    【讨论】:

    • 太棒了!不幸的是,这个功能在 9.1 中不存在 :(。我想知道是否有替代品...
    • 救命稻草。确认它可以像魅力一样在 9.4 上运行。有人应该帮助它进入文档。据我所见,还没有看到关于触发器WHEN (condition) 子句的任何有意义的示例。这绝对是合格的。
    • 它可以工作,但要注意,因为该函数不是测量递归的。如果其他触发器源自实际触发器,则该函数将生成 1 而不是 0。
    【解决方案4】:

    在触发器定义的开始,您可以禁用该特定表上的触发器,并在最后重新启用它们(并确保异常不会提前终止执行!)。这有很多深洞,但可能适用于一些轻量级的实现。请注意,对于此实现,您还需要权限来禁用触发器。

    【讨论】:

    • 这不起作用,因为:不能 ALTER TABLE "trigger_table" 因为它正在被此会话中的活动查询使用
    猜你喜欢
    • 2012-01-12
    • 2010-09-30
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-07
    • 2017-04-26
    相关资源
    最近更新 更多