sql server中try...catch仅捕获严重级别11–19的运行时错误,不捕获编译期错误(如语法错误、表或列不存在)和≥20的致命错误;必须在同一批处理中定义,且需关闭set xact_abort on才能生效。

SQL Server 中 TRY...CATCH 能捕获哪些错误
TRY...CATCH 只捕获严重级别 11–19 的运行时错误,不捕获编译期错误(比如语法错、表不存在)或严重级别 ≥20 的致命错误(如死锁、资源耗尽)。这意味着 SELECT * FROM NonExistentTable 在批处理开头就报错,根本进不了 TRY 块。
- 能捕获:
INSERT违反主键、CONVERT类型转换失败、除零(1/0)、事务中显式THROW - 不能捕获:
SELECT引用不存在列(编译阶段报错)、GO后的独立批处理错误、严重级别 10 及以下的警告(如空值参与聚合) - 注意:
SET XACT_ABORT ON会让某些错误(如约束冲突)自动终止批处理,绕过 CATCH —— 必须关掉它才能让 TRY...CATCH 生效
必须在同一个批处理中定义和调用 TRY...CATCH
SQL Server 的 TRY...CATCH 作用域严格绑定于当前批处理(batch),跨 GO 就失效。常见误写是把 CATCH 放在另一个 GO 之后,结果异常直接抛出,毫无拦截。
- ✅ 正确:所有
BEGIN TRY/BEGIN CATCH/END CATCH必须在同一个GO分隔区内 - ❌ 错误:
GO出现在END TRY和BEGIN CATCH之间 - 存储过程中无需
GO,整个过程体天然是一个批处理,但若你在 SSMS 中调试时手动加了GO,就会意外切断作用域
在 CATCH 块里获取真实错误信息要靠系统函数
ERROR_MESSAGE()、ERROR_LINE() 这些函数只在 CATCH 块内有效,且每次只返回最近一次错误的信息 —— 如果 CATCH 里又出错(比如日志表被删了),原始错误就被覆盖了。
- 必用函数:
ERROR_NUMBER()、ERROR_SEVERITY()、ERROR_STATE()、ERROR_PROCEDURE()、ERROR_LINE()、ERROR_MESSAGE() - ⚠️ 风险点:不要在 CATCH 里执行可能失败的操作(如写入不可达的链接服务器表),否则会丢失原始错误上下文
- 建议模式:先把关键错误信息存到局部变量(如
@ErrMsg = ERROR_MESSAGE()),再做后续逻辑
事务状态不一致时,ROLLBACK 要加判断
进入 CATCH 不代表事务一定处于可回滚状态。如果错误导致事务被标记为“不可提交”(XACT_STATE() = -1),强行 COMMIT 会报错;而 XACT_STATE() = 0 表示已自动回滚,再执行 ROLLBACK 会失败。
- 安全写法:先查
IF XACT_STATE() 0 ROLLBACK TRANSACTION - 区分场景:
XACT_STATE() = 1表示事务可提交(少见),= -1表示必须回滚,= 0表示无活动事务 - 别依赖
@@TRANCOUNT判断:它在某些错误下可能仍 > 0,但事务实际已不可用
事务嵌套、异步操作、分布式事务里的错误传播,远比单层 TRY...CATCH 复杂——这些地方的错误往往根本进不了你的 CATCH 块。










