mysql报error 1442语法级禁止同表dml,sql server静默递归致卡死或嵌套超限,postgresql默认递归需用pg_trigger_depth()手动拦截。

ERROR 1442 拦住,SQL Server 默默递归卡死,PostgreSQL 则默认放行但不拦,全靠你自己加判断。
MySQL 触发器报 ERROR 1442 就是语法级拒绝
这不是循环,是 MySQL 主动不让你干这事:AFTER UPDATE 里写 UPDATE users SET status = 'done' WHERE id = NEW.id,立刻抛出 ERROR 1442 (HY000): Can't update table 'users' in stored function/trigger...。
别试 IF OLD.status != NEW.status——只要语句里有对本表的 DML,就触发拦截。
正确做法:
- 该改值的,用
BEFORE UPDATE+SET NEW.status = 'done' - 该落库记日志的,写进中间表:
INSERT INTO users_trigger_queue (user_id, action) VALUES (NEW.id, 'update') - 真要跨表更新(比如同步
orders改了customers),必须设会话级限制:SET SESSION max_sp_recursion_depth = 1,且每次连接复用前重置
SQL Server 触发器不报错但会卡死或爆嵌套深度
它允许你在 AFTER UPDATE 里直接 UPDATE orders SET updated_at = GETDATE() WHERE order_id = inserted.order_id,但会再次触发自身。
现象是连接卡住、sp_who2 显示 LCK_M_U 等待,或者最终报 Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32)。RECURSIVE_TRIGGERS OFF 对这种 A→A 自调用完全无效。
可靠解法只有:
- 业务 SQL 开头设
SET CONTEXT_INFO 0x54524947474552(即 "TRIGGER" 的 ASCII 十六进制) - 触发器第一行检查:
IF CONTEXT_INFO() = 0x54524947474552 RETURN - 别用
@@NESTLEVEL或临时表——并行执行下不准,跨会话不可见
PostgreSQL 触发器默认递归,但给你留了自控接口
它不像 MySQL 那样硬拦,也不像 SQL Server 那样静默递归;你写 UPDATE 同表,它真会再触发一次——所以必须手动拦。
关键函数是 pg_trigger_depth(),首次触发返回 1,第二次返回 2。
触发器开头必须加:IF pg_trigger_depth() > 1 THEN RETURN; END IF;
更精细控制可结合 TG_OP:
- 只在
AFTER UPDATE场景下跳过递归 - 避免在
INSERT或DELETE时误判
跨表双触发器互相更新时,标记和禁用都得慎用
比如 t_users 和 tUsers 各有一个触发器,改密码就同步对方——两个触发器会互相唤醒。
用 DISABLE TRIGGER + ENABLE TRIGGER 风险极高:
- 并发请求可能互相干扰:A 禁用后还没启用,B 就进来了
- DDL 操作本身带锁,容易引发阻塞
- 恢复失败时状态不一致,没法回退
- SQL Server 用
CONTEXT_INFO - MySQL 用
@TRIGGER_DISABLED变量 + 触发器内守卫逻辑 - PostgreSQL 用
pg_trigger_depth()+TG_TABLE_NAME区分来源表











