sql更新触发死循环主因是触发器自修改监听表:mysql报error 1442属语法拦截,sql server静默递归致卡死,postgresql需用pg_trigger_depth()>1手动拦截;recursive_triggers开关对自引用无效。

SQL 更新操作触发死循环,不是因为“递归触发器开关”没开,而是因为你试图在触发器里改自己监听的那张表——RECURSIVE_TRIGGERS 对这种自引用完全无效,关了也没用,开了更危险。
MySQL 报 ERROR 1442 就是明确拒绝自更新
你在 AFTER UPDATE 里写 UPDATE users SET status = 'done' WHERE id = NEW.id,MySQL 直接拦住并抛出 ERROR 1442 (HY000): Can't update table 'users' in stored function/trigger because it is already used by statement which invoked this stored function/trigger。这不是死循环,是语法级硬拦截。
-
BEFORE触发器应优先用SET NEW.status = 'done'修改即将插入/更新的值,而不是发新 SQL -
AFTER里真要落库(比如记日志+同步状态),必须拆到中间表:INSERT INTO users_trigger_queue (user_id, action) VALUES (NEW.id, 'update') - 别信“加了
IF OLD.status != NEW.status就安全”——只要触发器内调用的函数、视图或跨库语句又碰回原表,照样崩
SQL Server 自引用更新不会报错,但会卡死或爆嵌套深度
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)。
-
SET RECURSIVE_TRIGGERS OFF对这种 A→A 自调用完全无效,别浪费时间试 - 可靠解法是用
CONTEXT_INFO做会话级标记:业务 SQL 开头设SET CONTEXT_INFO 0x54524947474552("TRIGGER" 的 ASCII 十六进制),触发器第一行就检查IF CONTEXT_INFO() = 0x54524947474552 RETURN - 避免用
@@NESTLEVEL或临时表判断——前者在并行执行下不准,后者跨会话不可见且需建表
PostgreSQL 默认不递归,但 pg_trigger_depth() 必须手动用
PostgreSQL 不像 MySQL 那样禁止自更新,也不像 SQL Server 那样默认放行;它允许触发器内 UPDATE 同表,但每次都会再触发——所以你得自己拦。关键函数是 pg_trigger_depth(),首次触发返回 1,第二次返回 2,以此类推。
- 触发器开头必须加
IF pg_trigger_depth() > 1 THEN RETURN; END IF; - 更精细控制可结合
TG_OP和TG_TABLE_NAME:比如只在AFTER UPDATE且目标表是orders时才检查深度 - 别依赖
current_setting('app.trigger_active')这类自定义变量——若未用SET LOCAL设置,会读到会话级默认值,不可靠
最易被忽略的一点:所有数据库的“递归”定义都不一样——MySQL 是语法拦截,SQL Server 是运行时嵌套计数,PostgreSQL 是深度函数可控。拿一个库的经验去套另一个,大概率踩坑。











