触发器必须运行在宿主语句的事务上下文中,不能独立提交或回滚;其内BEGIN TRANSACTION仅增加@@TRANCOUNT,COMMIT仅递减计数,ROLLBACK则回滚整个外部事务,SAVE TRANSACTION是唯一支持部分回滚的机制。

不能在触发器里显式开启或提交事务,但触发器天然运行在宿主语句的事务上下文中——它不是“能不能用事务”,而是“必须和宿主事务一起回滚或提交”。
触发器内 BEGIN TRANSACTION 会引发嵌套事务问题
SQL Server 允许你在触发器里写 BEGIN TRANSACTION,但它不会创建真正独立的事务。实际效果是:该语句只是增加 @@TRANCOUNT 计数,而 COMMIT 只是递减计数,直到为 0 才真正提交;ROLLBACK 则直接回滚到最外层事务起点。
- 如果你在触发器里执行
BEGIN TRANSACTION后又COMMIT,什么也不会提交——因为宿主事务还没结束 - 若触发器中执行
ROLLBACK TRANSACTION,整个外部事务(包括原始 DML 和所有已执行的触发器逻辑)都会被回滚 - 多个嵌套
BEGIN不会隔离状态,SAVE TRANSACTION是唯一能做部分回滚的机制
ROLLBACK 在触发器里的行为很危险
一旦触发器中执行 ROLLBACK TRANSACTION,不仅触发器自己改的数据会被撤销,原始 INSERT/UPDATE/DELETE 也会失效,且后续同一批处理中的语句全部跳过——这点极易被忽略。
- 错误示例:
IF NEW.status = 'invalid' THEN ROLLBACK; END IF;(MySQL 不支持此语法,但类似逻辑在 SQL Server 中常见) - 正确做法:用
RAISERROR或SIGNAL抛异常代替ROLLBACK,让上层决定是否回滚 - MySQL 触发器甚至不支持
ROLLBACK语句,只能靠报错中断(如SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid status';)
AFTER 和 BEFORE 触发器对事务的影响不同
BEFORE 触发器可以修改 NEW 行数据(MySQL)或 INSERTED/DELETED 表内容(SQL Server),这些修改属于同一事务;AFTER 触发器无法改原操作数据,但可安全读取并跨表更新——前提是注意锁竞争和死锁风险。
- MySQL 的
BEFORE UPDATE可直接赋值:SET NEW.updated_at = NOW(); - SQL Server 的
AFTER UPDATE若调用另一个表的UPDATE,可能因二级索引间隙锁顺序不一致引发死锁 - 避免在
AFTER触发器里做耗时操作(如发 HTTP 请求、写日志表未加索引),否则拖慢主事务响应
真正容易被忽略的点是:触发器不是独立执行单元,它的成败完全绑定宿主语句。哪怕你只在触发器里查了一张表,只要该查询被阻塞,整个 INSERT 就卡住——这不是代码写得不对,而是事务设计本身决定了它必须这样。

















