触发器“无限递归”实为数据库保护机制:mysql硬性禁止同表写入(error 1442),sql server默认栈深32需关闭双重递归开关,postgresql允许但栈溢出才报错,解法包括异步处理、标记字段、深度检测或改用before触发器。

触发器报“无限递归”错误,根本不是代码写错了,而是数据库主动拦下了它——你写的逻辑确实会自我循环,引擎在栈溢出前强制中断,这是保护机制,不是 bug。
MySQL 报 ERROR 1442:不能更新本表
这不是递归深度问题,是硬性禁止。只要触发器里出现 UPDATE tbl、INSERT INTO tbl 或 DELETE FROM tbl,且目标表和触发器所属表相同,立刻抛出:ERROR 1442: Can't update table 'xxx' in stored function/trigger because it is already used by statement which invoked this stored function/trigger。
-
max_sp_recursion_depth对该错误完全无效,调它没用 - 临时表、视图、子查询中隐式写入本表,照样被拦
- 封装进存储过程再调用,也一样报错
- 真正能绕过的路只有三条:用应用层统一处理、发消息异步解耦、或加标记字段(如
is_processing)在触发器开头跳过
SQL Server 报 “Maximum nesting level exceeded”
它默认允许间接递归,但栈深上限是 32 层。一旦触发链形成闭环(比如 A 表 INSERT → trg_A → UPDATE A → trg_A 再次进入),就会爆栈。报错可能不明显,常表现为连接中断、sp_who2 显示 LCK_M_U 等锁等待,或日志里出现 StackOverflowError。
- 必须同时关两个开关:
ALTER DATABASE [db] SET RECURSIVE_TRIGGERS OFF+EXEC sp_configure 'nested triggers', 0; RECONFIGURE - 只关
RECURSIVE_TRIGGERS不够,nested triggers是底层开关,影响整个实例 - 更细粒度的防护:在触发器开头加
IF TRIGGER_NESTLEVEL(OBJECT_ID(N'dbo.trg_name')) > 1 RETURN,注意参数必须是OBJECT_ID(),不能传字符串 -
INSTEAD OF触发器不走这套递归控制,但逻辑要重写,不能简单复制AFTER逻辑
PostgreSQL 报 “stack depth limit exceeded”
它不禁止递归,而是等你真递归到栈溢出才报错。典型场景是 AFTER UPDATE 触发器里又 UPDATE 同一张表,导致无限循环。
- 最稳妥解法是改用
BEFORE UPDATE触发器,在数据提交前就改NEW.updated_at,不发新 SQL - 若必须用
AFTER,得靠pg_trigger_depth()判断嵌套深度,或用会话级变量 - 常见错误写法:
UPDATE data SET updated_at = current_timestamp WHERE data.id = NEW.id—— 这会再次触发自身,直接死循环 -
pg_trigger_depth()返回值为 1 表示首次触发,≥ 2 就该RETURN
真正难处理的从来不是参数上限,而是触发器之间缺乏调用边界——一个 BEFORE UPDATE 改了字段,另一个 AFTER UPDATE 又监听该字段,稍不留神就形成闭环。与其反复调参,不如先画出触发器依赖图,砍掉非必要链。











