SQL Server触发器中TRY…CATCH可捕获大多数运行时错误,但须在同一批处理内连续书写、不可含GO;ERROR_*函数仅在CATCH内有效;需用THROW/RAISERROR重抛错误才能通知应用;不自动回滚,须手动检查XACT_STATE()并处理;对编译期错误无效。
SQL Server 触发器中不能直接用
捕获所有错误,但只要满足作用域和语法约束,它确实能捕获大多数运行时错误——关键在于必须把整个逻辑包进存储过程式结构里,且不能被
或批处理打断。
触发器内
必须写在同一个批处理中
触发器本身就是一个独立批处理单元,
和
必须连续出现,中间不能有
、不能跨批、也不能放在动态 SQL 字符串里执行。一旦出现
(比如调试时习惯性加的),SQL Server 就会报错:
。
不是 T-SQL 语句,而是客户端工具(如 SSMS)识别的批处理分隔符,它会让
和
被拆到两个批里,直接失效
触发器定义体(
后面的内容)天然就是一个完整批,无需也不该加
如果想在触发器里调用外部逻辑,应封装成存储过程再
,而不是把
拆出去
函数只能在
块内调用
像
、
、
这类函数,离开
块就返回
。很多人误以为可以在
块末尾或触发器结尾处“补查”错误,结果全为空值。
必须在
内部立即调用,例如
这些函数不保存历史状态,每个
只反映当前被捕获的那一次错误
若需记录多条错误,得靠
显式落库,不能依赖函数缓存
触发器中错误默认不传给应用程序
即使
成功捕获了错误,调用方(比如应用层的
)仍然收不到异常——因为 SQL Server 把错误“吞掉”了。除非你主动抛出。
要用
(SQL Server 2012+)或
重新抛出,才能让上层感知失败
更简洁:
(重抛原错误)或
用
时注意级别:低于 11 的错误不会中断执行,等于白写;推荐用 16
别只靠
或
输出,它们只是返回结果集,不触发异常传播
事务上下文与回滚行为要手动控制
触发器运行在隐式事务中(对应 INSERT/UPDATE/DELETE 操作),但
不会自动
。如果没显式处理,错误后继续执行可能破坏数据一致性。
在
块开头先检查事务状态:
返回 -1 表示不可提交事务(必须回滚),0 表示无活动事务,1 表示可提交
不要假设
就一定该回滚——嵌套事务下它可能为 2,但实际已损坏
回滚后若仍想通知应用,必须紧跟
,否则错误静默丢失
最易被忽略的一点:触发器里的
对编译期错误(比如表不存在、列名拼错)完全无效——这类错误在执行前就被拦截,根本进不了
块。只有运行时错误(如除零、违反约束、死锁)才会触发
。上线前务必用真实数据路径做 DML 测试,不能只看语法是否通过。
TRY…CATCHGOTRY…CATCHBEGIN TRYBEGIN CATCHGOGOIncorrect syntax near the keyword 'CATCH'GOTRYCATCHCREATE TRIGGER … ASGOEXECTRY…CATCHERROR_*()CATCHERROR_NUMBER()ERROR_MESSAGE()ERROR_SEVERITY()CATCHNULLTRYBEGIN CATCH … END CATCHSELECT ERROR_NUMBER(), ERROR_MESSAGE();CATCHINSERT INTO error_logCATCHSqlCommand.ExecuteNonQuery()THROWRAISERRORTHROWTHROW;THROW 50000, '自定义消息', 1;RAISERRORPRINTSELECTTRY…CATCHROLLBACKCATCHIF XACT_STATE() 0 ROLLBACK TRANSACTION;XACT_STATE()@@TRANCOUNT > 0THROWTRY…CATCHTRYCATCH