SQL Server触发器中COMMIT无效,仅递减@@TRANCOUNT;ROLLBACK会终止整个宿主事务;唯一支持部分回滚的是SAVE TRANSACTION配合ROLLBACK TO。

SQL Server 触发器里写 COMMIT TRANSACTION 不会报错,但完全无效——它只递减 @@TRANCOUNT,而不会真正提交事务;真正提交的时机由外部 DML 语句决定,触发器无权提前交出控制权。
触发器没有独立事务,只有嵌套计数
触发器运行在宿主 INSERT/UPDATE/DELETE 的事务上下文中,不是新事务起点。你写 BEGIN TRANSACTION 只会让 @@TRANCOUNT 从 1 变成 2;COMMIT 则让它变回 1,仅此而已。只有当 @@TRANCOUNT 归零时,SQL Server 才真正提交——而这一步永远发生在外部语句执行完之后。
- 在
AFTER INSERT触发器里执行COMMIT,对主 INSERT 是否成功毫无影响 - 如果外部语句是隐式事务(如单条
UPDATE),COMMIT后@@TRANCOUNT= 0,但此时事务尚未结束,因为 SQL Server 要等整个语句生命周期走完才提交 - 若触发器内
COMMIT后又执行了另一条INSERT,它仍属于原始事务,不是“新事务”
ROLLBACK 在触发器里会直接终结整个外部事务
ROLLBACK TRANSACTION 是唯一能立即生效的事务控制语句,但它不“局部回滚”,而是把整个宿主事务(包括原始 DML 和所有已执行的触发器逻辑)一并撤销。这是最危险也最容易误用的行为。
- 执行
ROLLBACK后,原始INSERT数据不会落库,后续同一批语句(如批量导入中的下一行)也会被跳过 - 哪怕你只在触发器里查了一张表,只要该查询被阻塞,整个宿主语句就卡住——这不是代码问题,是事务设计的必然
- 嵌套触发器中
@@TRANCOUNT累加到 3,一次ROLLBACK就清零,无法只撤回某一层的操作
SAVE TRANSACTION 是唯一支持部分回滚的机制
如果你真需要“只撤回触发器自己做的修改”,必须用 SAVE TRANSACTION 设保存点,再配合 ROLLBACK TO。但注意:这只能回滚到保存点,不能提交或隔离后续操作。
-
SAVE TRANSACTION sp_log必须在触发器开头显式声明,且名字不能重复 -
ROLLBACK TO sp_log只撤销从该点之后的变更,原始 DML 仍继续执行(除非你额外抛错中断) - 不能在
TRY...CATCH外部设保存点后跨块回滚;保存点作用域限于当前批处理 - MySQL 完全不支持
SAVE TRANSACTION,它的触发器连ROLLBACK都禁止
真正容易被忽略的点
触发器不是“一段可自由控制事务的代码”,它是一段强制挂载在事务末尾的逻辑。你写的每一条 SELECT、UPDATE、甚至 PRINT,都和主语句共享锁、日志、回滚段——哪怕只是读一张配置表,也可能因锁等待拖垮整条 INSERT。










