SQL Server触发器中“忽略错误”需在TRY…CATCH内显式处理并RETURN,禁止THROW/RAISERROR/ROLLBACK,避免跨库操作,关键逻辑应异步化,确保主DML不受影响。

触发器里用 TRY…CATCH 捕获错误但不抛出
SQL Server 触发器默认是事务性上下文的一部分,只要触发器里发生未捕获的错误(比如 INSERT 失败、RAISERROR 未处理),整个外部语句就会回滚。想“忽略错误”,核心不是绕过事务机制,而是让触发器自己消化异常,不让它冒泡出去。
必须在触发器主体内使用 TRY...CATCH,且 CATCH 块中不能再次 THROW 或 RAISERROR(除非带 WITH LOG 且明确不中断)。常见错误是只写 PRINT 或留空 CATCH,这看似“忽略”,但若原错误是严重级 ≥11 的运行时错误(如主键冲突、类型转换失败),仍会终止执行——必须显式用 RETURN 或后续语句收尾。
-
TRY块里放可能出错的逻辑(如写日志表、调用存储过程、更新关联表) -
CATCH块里仅记录错误(写入sys.dm_exec_sessions或自定义日志表)、RETURN,不要ROLLBACK(触发器没自己的事务,ROLLBACK会连主事务一起干掉) - 避免在
CATCH中执行同样可能失败的操作(比如又往一个空间不足的日志表INSERT)
避免触发器里做跨数据库或远程操作
触发器执行期间若访问其他数据库(尤其是不同实例的链接服务器),一旦网络抖动或目标不可达,会直接报 OLE DB provider "SQLNCLI11" for linked server 类错误,且无法被普通 TRY...CATCH 捕获(属于严重级 16+ 的连接级错误)。这类操作本质就不该放在触发器里。
替代方案是把耗时/高风险动作解耦:触发器只写一条消息到本地队列表(INSERT INTO dbo.TriggerQueue),再由 SQL Agent 作业或外部服务轮询处理。这样主 DML 完全不受影响,错误也只影响异步任务。
- 禁止在触发器中使用
OPENQUERY、EXEC(@sql) AT [LinkedServer] - 如果必须调用同实例其他库的对象,确保目标库存在且用户有权限,优先用三段名(
[OtherDB].[schema].[table])而非动态拼接 - 对
INSERT/UPDATE/DELETE目标表本身的操作,注意避免触发递归(检查@@NESTLEVEL或关闭RECURSIVE_TRIGGERS)
慎用 AFTER 触发器中的 ROLLBACK TRANSACTION
有人误以为在 AFTER 触发器里加 IF @@ERROR 0 ROLLBACK 能“控制回滚范围”,这是危险操作。触发器和主语句共享同一事务,ROLLBACK 会回滚整个批处理,包括主 INSERT 本身——这恰恰违背了“不影响主流程”的目标。
真正需要的是“尽力而为”:比如同步更新缓存表失败,就记日志然后继续;而不是因为缓存表挂了,导致订单插入失败。所有写操作都应设为非关键路径,且具备幂等性(重试不重复)。
-
AFTER触发器中绝对不要出现ROLLBACK或COMMIT - 若业务上真需根据条件阻止主操作(如校验不通过禁止插入),应该用
INSTEAD OF触发器,并在其中显式INSERT或RETURN,而非依赖错误中断 - 对关键数据一致性要求高的场景,触发器不是首选——考虑用应用层约束、CHECK 约束或物化视图
测试时重点验证错误传播级别
本地 RAISERROR('msg', 10, 1) 可被 TRY...CATCH 捕获,但 RAISERROR('msg', 16, 1) 或违反约束(如 INSERT 主键重复)产生的错误,是否真的被吞掉?必须实测。方法是:在触发器里故意写一条必然失败的语句(如向只有 INT 列的表插字符串),然后执行主 UPDATE,查主表数据是否已提交。
- 用
SELECT XACT_STATE()在CATCH中确认事务状态(-1=不可提交,0=已提交,1=可提交);正常忽略场景下应为 1 - 在触发器开头加
SET XACT_ABORT OFF(默认就是 OFF),避免某些 SET 选项干扰 - 生产环境触发器上线前,务必在相同版本 SQL Server 上用真实负载压测,观察阻塞链和日志增长

















