不能。TRY...CATCH仅捕获严重级别11–19的运行时错误,不捕获编译期错误(如语法错、对象不存在)、≥20的致命错误及RAISERROR级别≤10的信息性消息;需配合XACT_STATE()判断事务状态、ERROR_*函数获取准确上下文,并避免CATCH中执行二次出错操作。
不能。SQL Server的TRY...CATCH只捕获严重级别11–19的运行时错误,不捕获编译期错误(如语法错误、对象不存在)、严重级别20+的致命错误(如连接中断),也不捕获RAISERROR级别10及以下的提示性消息。
常见误判场景:执行SELECT * FROM NonExistentTable会在编译阶段报错,根本进不了TRY块;而RAISERROR('msg', 10, 1)不会触发CATCH——必须用RAISERROR('msg', 11, 1)或更高。
关键点在于判断事务状态,避免COMMIT或ROLLBACK在无效状态下执行。SQL Server提供XACT_STATE()函数返回当前事务状态:1表示可提交,-1表示需回滚,0表示无活动事务。
CATCH块开头立即调用XACT_STATE(),不要依赖@@TRANCOUNT
XACT_STATE() = -1,必须ROLLBACK,否则后续语句可能失败XACT_STATE() = 1,可选择COMMIT或ROLLBACK,取决于业务逻辑CATCH末尾用THROW重新抛出错误(SQL Server 2012+),保留原始错误号和行号BEGIN TRY BEGIN TRANSACTION; UPDATE Accounts SET Balance -= @amount WHERE ID = @accountID; UPDATE Accounts SET Balance += @amount WHERE ID = @targetID; COMMIT TRANSACTION;END TRYBEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; THROW; -- 保留原始错误上下文END CATCH
因为@@ERROR是“上一条语句”的错误号,而CATCH块内第一条语句(比如DECLARE)会覆盖它。必须改用ERROR_NUMBER()等内置错误函数,它们在CATCH作用域内稳定返回本次错误信息。
ERROR_NUMBER() → 替代@@ERROR
ERROR_SEVERITY() → 获取严重级别ERROR_LINE() → 定位到TRY块内出错的具体行号ERROR_MESSAGE() → 比@@ERROR更完整的文本描述CATCH外调用返回NULL,不能跨作用域缓存把错误信息写入日志表时,ERROR_MESSAGE()返回nvarchar(4000),但若日志字段定义为varchar(500),SQL Server会静默截断——不会报错,也不会警告。
nvarchar(max)或至少nvarchar(4000)
CONCAT(ERROR_MESSAGE(), ' at line ', ERROR_LINE())比ERROR_MESSAGE() + ' at line ' + CAST(ERROR_LINE() AS VARCHAR)更安全CATCH里调用可能失败的复杂逻辑(如远程查询、链接服务器操作),否则可能掩盖原始错误健壮的错误处理真正难的不是结构,而是意识到TRY...CATCH本身有边界——它管不了编译、管不了连接、管不了资源耗尽,而这些恰恰是生产环境最常出问题的地方。