结论:递归触发器报错是数据库的保护机制而非bug,mysql硬性禁止同表操作,sql server默认栈深32需关双开关,postgresql靠深度检测或改用before触发器,oracle需结合trace定位真实原因。

MySQL 报 ERROR 1442:不能更新本表
这不是递归深度问题,是硬性禁止。MySQL 在触发器里执行 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是底层开关,影响整个实例 - 在触发器里用
TRIGGER_NESTLEVEL(OBJECT_ID(N'dbo.trg_name')) > 1守卫更细粒度,但注意参数必须是OBJECT_ID(),不能传字符串 - INSTEAD OF 触发器不走这套递归控制,但逻辑要重写,不能简单复制 AFTER 逻辑
PostgreSQL 报 “stack depth limit exceeded”
它不禁止递归,而是等你真递归到栈溢出才报错。典型场景是 AFTER UPDATE 触发器里又 UPDATE 同一张表,导致无限循环。
- 最稳妥解法是改用
BEFORE UPDATE触发器,在数据提交前就改NEW.updated_at,不发新 SQL - 若必须用 AFTER,得靠
pg_trigger_depth()判断嵌套深度,或用会话级变量(SET LOCAL trigger.depth = 1)做标记 -
max_stack_depth只是治标,调大后仍可能耗尽内存,且掩盖设计缺陷 - 跨表触发链(A→B→A)比自调用更难发现,上线前得用单行 DML +
SELECT pg_trigger_depth()验证
Oracle 报 ORA-00604:递归 SQL 中发生错误
这个错误本身不指向触发器,而是说“数据库内部执行某条递归 SQL 时失败了”。它可能是触发器引起的,也可能来自权限检查、数据字典查询或登录触发器。
- 必须看伴随错误,比如
ORA-00942(表不存在)、ORA-04031(共享池不足)、ORA-01555(快照过旧) - 启用
10046 trace是唯一可靠手段,ALTER SESSION SET EVENTS '10046 trace name context forever, level 12' - 检查失效对象:
SELECT object_name, object_type FROM dba_objects WHERE status = 'INVALID' - 登录触发器如果写了复杂逻辑或远程调用,极易引发 ORA-00604,建议拆到应用层











