try catch 是 sql server 存储过程中处理运行时错误的唯一可靠机制,必须位于同一批处理内,catch 中须用 error_*() 函数即时捕获错误信息,需显式 rollback 事务,并推荐使用 throw 原样重抛异常。

TRY CATCH 在 SQL Server 存储过程中不是“可选增强”,而是处理运行时错误的**唯一可靠机制**。它不能替代逻辑校验,但能兜住 INSERT 冲突、除零、类型转换失败、死锁回滚等典型异常。
TRY CATCH 必须写在同一个批处理里
常见错误是把 BEGIN TRY 和 BEGIN CATCH 用 GO 隔开——这会直接报语法错误:Incorrect syntax near 'CATCH'。
-
GO是客户端命令,不是 T-SQL 语句,它会切断批处理上下文 -
TRY块和紧随其后的CATCH块必须属于同一作用域(同一存储过程、同一脚本块) - 嵌套也一样:内层
TRY和它的CATCH也不能被GO或其他批处理分隔
CATCH 块里只能用 ERROR_*() 函数
这些函数在 CATCH 外调用一律返回 NULL,哪怕只差一行——这是最容易忽略的陷阱。
-
ERROR_NUMBER()返回错误编号,比如除零是8134,主键冲突是2627 -
ERROR_MESSAGE()返回完整消息文本,含占位符(如'Violation of %ls constraint %.*ls. Cannot insert duplicate key in object %.*ls.'),不带参数值 -
ERROR_LINE()返回出错语句所在行号,注意:是TRY块内的相对行号,不是整个存储过程的绝对行号 - 想记录日志或传给上层,必须在
CATCH块内立刻取值,存到变量里再用
事务回滚必须显式写 ROLLBACK
TRY CATCH 不会自动回滚事务。如果 TRY 中已 BEGIN TRANSACTION,出错后连接仍处于活动事务状态,不手动 ROLLBACK 就提交,数据就错了。
- 推荐模式:
IF @@TRANCOUNT > 0 ROLLBACK;放在CATCH开头 - 别依赖
XACT_ABORT ON——它只对某些严重错误生效,且不保证所有语句原子性 - 如果存储过程可能被嵌套调用,还要检查
@@TRANCOUNT是否为 0 再决定是否ROLLBACK
THROW 比 RAISERROR 更安全地重抛异常
旧写法用 RAISERROR 手动构造错误,容易丢原始行号、错误号、严重级;THROW(SQL Server 2012+)能原样重抛当前上下文错误。
- 直接写
THROW;(无参数)就能复现原始错误,包括ERROR_LINE()和堆栈位置 - 若要自定义消息但保留原始错误号,用
THROW 50000, '自定义提示', 1;,但注意错误号必须 ≥ 50000 -
THROW后会立即退出当前作用域,后续语句不执行;而RAISERROR默认不终止流程,需加WITH LOG或配合RETURN
BEGIN TRY,而是判断该不该吞掉错误、该不该重抛、该不该记录到表里、事务边界是否清晰——这些都得结合业务语义来定,没通用解。










