上一篇
场景再现:凌晨三点,你正美梦正酣,突然被急促的报警短信惊醒——"订单服务崩溃!数据库存储过程执行失败!" 😱 翻开日志发现是某个存储过程因数据冲突抛出了异常,此时若能优雅捕获异常并自动处理,或许就能避免这场午夜惊魂...
SQL Server存储过程就像后厨的厨师👨🍳,一旦食材(数据)有问题(比如外键冲突/空值插入),默认会直接摔锅罢工(抛出错误),通过TRY-CATCH
机制,我们可以:
CREATE PROCEDURE usp_PlaceOrder AS BEGIN BEGIN TRY BEGIN TRANSACTION; -- 业务逻辑代码 INSERT INTO Orders(CustomerID, OrderDate) VALUES (999, GETDATE()); -- 假设客户ID不存在会触发外键冲突 COMMIT TRANSACTION; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK; -- 获取错误信息 SELECT ERROR_NUMBER() AS ErrorCode, ERROR_MESSAGE() AS ErrorText, ERROR_PROCEDURE() AS FailedProc; -- 可选:记录到错误表 INSERT INTO ErrorLog(ErrorTime, UserName, ErrorDetails) VALUES (GETDATE(), SYSTEM_USER, '错误#' + CAST(ERROR_NUMBER() AS VARCHAR) + ': ' + ERROR_MESSAGE()); END CATCH END;
关键函数🔧:
ERROR_LINE()
→ 出错行号 ERROR_SEVERITY()
→ 错误严重等级 ERROR_STATE()
→ 错误状态码 复杂存储过程可能需要多层捕获,就像俄罗斯套娃🪆:
BEGIN TRY -- 外层处理业务异常 EXEC usp_ProcessPayment; BEGIN TRY -- 内层处理数据校验 EXEC usp_ValidateInventory; END TRY BEGIN CATCH -- 仅处理库存类错误 IF ERROR_NUMBER() = 547 -- 约束冲突 RAISERROR('库存不足,已自动取消订单', 16, 1); END CATCH END TRY BEGIN CATCH -- 处理支付等核心错误 IF ERROR_NUMBER() IN (2601, 2627) -- 主键/唯一键冲突 EXEC usp_RetryPayment @MaxAttempts=3; ELSE THROW; -- 重新抛出未处理异常 END CATCH
EXEC xp_logevent 50001, '订单处理失败', ERROR;
EXEC msdb.dbo.sp_send_dbmail @subject='数据库警报', @body=ERROR_MESSAGE();
@@TRANCOUNT
再回滚 THROW
会保留原始错误堆栈(SQL 2012+) RAISERROR
可自定义消息但会覆盖原始错误 BEGIN TRY SET LOCK_TIMEOUT 5000; -- 5秒锁等待 -- 你的代码 END TRY BEGIN CATCH IF ERROR_NUMBER() = 1222 -- 锁超时 EXEC usp_QueueForRetry; END CATCH
CREATE PROCEDURE usp_SubmitOrder @CustomerID INT, @ProductID INT AS BEGIN DECLARE @RetryCount TINYINT = 0; RetryPoint: BEGIN TRY BEGIN TRANSACTION; -- 检查客户状态 IF NOT EXISTS (SELECT 1 FROM Customers WHERE CustomerID = @CustomerID) RAISERROR('客户不存在', 16, 1); -- 扣减库存(可能死锁) UPDATE Products SET Stock = Stock - 1 WHERE ProductID = @ProductID AND Stock > 0; IF @@ROWCOUNT = 0 RAISERROR('库存不足', 16, 2); COMMIT TRANSACTION; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK; -- 死锁自动重试3次 IF ERROR_NUMBER() = 1205 AND @RetryCount < 3 BEGIN SET @RetryCount += 1; WAITFOR DELAY '00:00:01'; -- 等待1秒 GOTO RetryPoint; END -- 其他错误记录并通知 EXEC usp_LogError @ProcedureName='usp_SubmitOrder'; THROW; -- 让应用程序捕获 END CATCH END;
:好的异常处理就像数据库的保险丝⚡——平时看不见,出事时能保住整个系统,记住三个原则:
下次数据库再闹脾气,你就能淡定地说:"小问题,看我CATCH住它!" 😎
本文由 苗凡白 于2025-08-02发表在【云服务器提供商】,文中图片由(苗凡白)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/517626.html
发表评论