SQL Server TRY/CATCH仅捕获严重性>10的运行时错误(如除零、约束冲突、转换失败、死锁),不捕获语法错误、延迟解析失败、严重性≤10错误;需配合事务显式回滚,并仅在CATCH中使用ERROR_*函数。

SQL Server 的 TRY/CATCH 不能捕获所有错误,必须明确哪些能抓、哪些根本进不了 CATCH 块。
TRY/CATCH 能捕获哪些错误
它只捕获严重性(ERROR_SEVERITY())大于 10 的运行时错误,且该错误不能导致连接中断。典型可捕获场景包括:
-
SELECT 1/0(除零) -
INSERT INTO ... VALUES (...)违反约束(如主键冲突、NOT NULL 插入 NULL) -
CONVERT(DATETIME, 'invalid')类型转换失败 - 死锁(错误号
1205),常用于重试逻辑
注意:RAISERROR(..., 11, 1) 显式抛出的错误也能进 CATCH;但 RAISERROR(..., 10, 1) 不会中断执行,也进不了 CATCH。
TRY/CATCH 捕捉不到的错误类型
这些错误在语法解析或编译阶段就失败,根本不会执行到 TRY 块内部:
- 语法错误,例如
SELEC * FROM table(少了个 T)——批处理直接报错退出 - 对象不存在且启用延迟名称解析(如引用了还没创建的表),错误发生在执行时但不在 TRY 范围内
- 严重性 ≤10 的错误,比如
PRINT或低级别警告 - 某些编译期错误(如存储过程中引用了不存在的列,且未开启 ANSI_WARNINGS)
这类错误无法靠 TRY/CATCH 拦截,只能靠静态检查、单元测试或部署前脚本校验。
事务与 TRY/CATCH 必须配对使用
仅用 TRY/CATCH 不等于自动回滚。如果 TRY 块里开了事务但没显式 COMMIT 或 ROLLBACK,出错后事务仍处于活动状态,可能造成连接阻塞或后续语句失败。
标准写法是:
BEGIN TRY BEGIN TRANSACTION INSERT INTO dbo.Sale VALUES (100, GETDATE(), 5) INSERT INTO dbo.Sale VALUES (200, 'bad date', 3) -- 触发错误 COMMIT TRANSACTION END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION THROW; -- 重新抛出原始错误给调用方 END CATCH
关键点:
- 必须检查
@@TRANCOUNT再ROLLBACK,否则可能误操作外部事务 - 用
THROW(无参数)比RAISERROR更好,能保留原始错误号、行号和消息 - 不要在 CATCH 中盲目
COMMIT,错误发生后数据通常已不一致
CATCH 块里能安全调用的系统函数
只有在 CATCH 块内部,这些函数才返回有意义的值:
-
ERROR_NUMBER()—— 错误号,如8134(除零) -
ERROR_MESSAGE()—— 完整错误文本 -
ERROR_SEVERITY()—— 严重级别 -
ERROR_STATE()—— 错误状态码 -
ERROR_LINE()—— 出错语句所在行号(在存储过程中有效) -
ERROR_PROCEDURE()—— 当前存储过程名(若在过程中)
这些函数在 CATCH 外调用一律返回 NULL 或 0。别指望在 TRY 块里提前读取它们。
最易被忽略的是:TRY/CATCH 不能跨批处理,也不能嵌套在 IF...ELSE 或独立 BEGIN...END 块之间。写完立刻跟 END TRY BEGIN CATCH,中间不能插任何语句,否则报语法错误。

















