SQL Server触发器中ROLLBACK会回滚整个外部事务,包括原始DML及所有触发器逻辑;MySQL不支持ROLLBACK需用SIGNAL,PostgreSQL用RAISE EXCEPTION;autocommit=true会使所有回滚失效。

触发器里写ROLLBACK会直接回滚整个外部事务
SQL Server 允许在触发器中写 ROLLBACK TRANSACTION,但它不是“回滚触发器自己”,而是清空整个外层事务的 @@TRANCOUNT,连同原始 DML 语句、所有已执行的触发器逻辑、甚至后续同一批语句一起丢弃。MySQL 根本不支持该语句,执行即报错 ERROR 1422;PostgreSQL 则用 RAISE EXCEPTION 替代,它隐式终止当前事务,效果等价。
SQL Server 中必须搭配 RAISERROR 或 THROW 显式中断
只写 ROLLBACK TRANSACTION 不够——客户端可能收不到错误、上层应用误判为成功。必须组合使用:
-
RAISERROR('校验失败', 16, 1)后紧跟ROLLBACK TRANSACTION,且确保它一定执行(比如放在条件分支末尾,不被IF漏掉) - 更推荐
THROW 50000, '禁止删除', 1+SET XACT_ABORT ON,这样能强制终止整个批处理,避免事务挂起 - 别在
TRY...CATCH的CATCH块里只RAISERROR而不ROLLBACK,否则事务仍处于活动状态,可能阻塞其他连接
MySQL 和 PostgreSQL 必须用 SIGNAL / RAISE 强制中断
MySQL 的 SIGNAL SQLSTATE '45000' 在 BEFORE 触发器中会自动回滚主语句,但仅限于该语句本身;AFTER 触发器中无法阻止数据变更,也禁止 SIGNAL。PostgreSQL 的 RAISE EXCEPTION 行为类似,但注意它不能捕获后重试——一旦抛出,事务立即终止,没有“补救窗口”。
- MySQL 示例:
IF OLD.status = 'active' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '活跃状态禁止删除'; END IF; - PostgreSQL 示例:
IF OLD.status = 'active' THEN RAISE EXCEPTION '活跃状态禁止删除'; END IF; - 这两者都只能放在
BEFORE触发器中,AFTER阶段再拦截已无意义
真正容易被忽略的点:autocommit=true 让所有 ROLLBACK 失效
无论哪个数据库,只要连接处于自动提交模式,ROLLBACK 或 SIGNAL 都不会回滚任何东西——因为单条 DML 执行完就立刻提交了,根本不存在可回滚的事务上下文。
- SQL Server 驱动(如
go-mssqldb)默认autocommit=true,需显式加autocommit=false到连接字符串 - MySQL 默认
autocommit=1,必须先执行SET autocommit = 0或用BEGIN TRANSACTION包裹 DML - DDL 操作(如
TRUNCATE TABLE)、LOCK TABLES、甚至某些SELECT FOR UPDATE也会隐式提交当前事务,导致前面的ROLLBACK无效

















