各数据库禁止触发器直接修改自身监听表:MySQL语法层硬拦截报ERROR 1442;SQL Server依赖nested triggers开关及TRIGGER_NESTLEVEL()控制;PostgreSQL默认允许但需pg_trigger_depth()主动退出;SQLite完全禁止且无绕过方式。

直接在触发器里 UPDATE、INSERT 或 DELETE 自己监听的表,一定会失败——MySQL 报 ERROR 1442,SQL Server 静默递归直到栈溢出,PostgreSQL 默认放行但必须手动拦截,SQLite 则直接拒绝并报 cannot modify <table_name> because it is being used by trigger。这不是配置漏关或权限问题,是各数据库对执行上下文的硬性保护机制。
MySQL 触发器里改本表为什么死活不行
MySQL 在语法解析阶段就拦截:只要触发器体中出现对当前表的 DML 操作(哪怕只是 UPDATE t SET x = 1 WHERE id = NEW.id),立刻抛出 ERROR 1442。它不看你有没有加 IF 判断,也不管你是不是只更新一行。
-
RECURSIVE_TRIGGERS这个选项根本不存在于 MySQL,别搜了 - 把 UPDATE 封装进存储过程再调用?照样报错
- 用临时表、视图、子查询间接写本表?全被拦住
- 唯一绕过路径:触发器只写
INSERT INTO event_queue,由外部消费者处理后续逻辑
SQL Server 怎么防触发器自己调自己
RECURSIVE_TRIGGERS OFF 只禁直接递归(比如 A 表 INSERT → A 表 AFTER INSERT → 再 INSERT A 表),但拦不住间接递归(A 表 INSERT → 触发器 UPDATE B 表 → B 表也有 AFTER UPDATE 触发器 → 又 UPDATE A 表)。真正起作用的是 nested triggers 实例级开关。
- 查当前值:
EXEC sp_configure 'nested triggers',返回 1 表示启用 - 彻底禁用:
EXEC sp_configure 'nested triggers', 0; RECONFIGURE - 更细粒度控制:在触发器开头加
IF TRIGGER_NESTLEVEL() > 1 RETURN - 最可靠标记法:
SET CONTEXT_INFO 0x54524947474552(即 "TRIGGER" 的 hex),触发器里用CONTEXT_INFO()检查,避免字符串转换截断
PostgreSQL 和 SQLite 的现实约束
PostgreSQL 不禁止递归,但提供 pg_trigger_depth() 让你主动退出;SQLite 则完全不支持,连商量余地都没有——AFTER 触发器里任何对本表的 DML 都被拒。
- PostgreSQL 推荐写法:
IF pg_trigger_depth() > 1 THEN RETURN NEW; END IF;,配合TG_OP做事件类型过滤 - SQLite 没有 pragma 能打开递归,
PRAGMA recursive_triggers = ON只影响INSTEAD OF触发器,对普通触发器无效 - SQLite 真正可行方案只有两个:把逻辑提到应用层事务里统一执行,或用临时表
INSERT INTO temp.pending_updates记录待处理项,由外部批量处理 - 外键级联(如
ON DELETE CASCADE)和触发器共存时,子表每删一行都进一次触发器——这是最常被忽略的隐性递归源
所有运行时标记方案(CONTEXT_INFO、session 变量、临时表)都依赖上下文一致性:连接池复用、事务中断未清理、存储过程嵌套调用,都可能让标记失效。递归问题从来不是“怎么开开关”,而是“谁在什么条件下会再次激活它”——业务逻辑变更、DBA 手动补数据、甚至迁移脚本,都可能突然把它点着。

















