T-SQLTRY...CATCH結構化異常處理中,系統函數ERROR_MESSAGE()、ERROR_SEVERITY()、ERROR_STATE()配合RAISERROR重拋,構建標準錯誤處理閉環。本文提供生產級模板與關鍵函數解析。
BEGIN TRY
-- 業務邏輯:可能拋錯的 SQL
INSERT INTO orders (user_id, amount) VALUES (100, -50); -- 假設 amount 有 CHECK 約束
UPDATE accounts SET balance = balance - 50 WHERE user_id = 100;
END TRY
BEGIN CATCH
-- 1. 獲取錯誤資訊
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
-- 2. 記錄日誌(可選:寫入錯誤日誌表)
-- INSERT INTO error_log (msg, severity, state, proc, time)
-- VALUES (@ErrorMessage, @ErrorSeverity, @ErrorState, ERROR_PROCEDURE(), GETDATE());
-- 3. 重拋錯誤給調用端(保持原始嚴重級別與狀態)
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH;
| 函數 | 返回值 | 說明 |
|---|---|---|
ERROR_NUMBER() | INT | 錯誤號(如 547 外鍵衝突, 2627 唯一鍵衝突) |
ERROR_SEVERITY() | INT | 嚴重級別 1-25(>10 為系統錯誤,16-18 可由用戶修復,19-25 需管理員) |
ERROR_STATE() | INT | 狀態碼,同一錯誤不同觸發點有不同州,助定位 |
ERROR_PROCEDURE() | SYSNAME | 發生錯誤的存儲過程/觸發器名(動態SQL中為NULL) |
ERROR_LINE() | INT | 批次/過程內行號 |
ERROR_MESSAGE() | NVARCHAR(4000) | 完整錯誤文本(含參數值,如欄位名、表名) |
BEGIN TRY
BEGIN TRANSACTION;
BEGIN TRY
-- 子操作1
INSERT ...
END TRY
BEGIN CATCH
-- 子操作1失敗邏輯
ROLLBACK TRANSACTION;
THROW; -- SQL Server 2012+ 推薦用 THROW 重拋,保留原始堆疊
END CATCH
BEGIN TRY
-- 子操作2
UPDATE ...
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
THROW;
END CATCH
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
-- 統一錯誤處理
DECLARE @msg NVARCHAR(4000) = ERROR_MESSAGE();
RAISERROR(@msg, 16, 1); -- 或 THROW
END CATCH
THROW:無需聲明變量,自動保留原始錯誤號、嚴重級別、狀態、堆疊;語法簡潔THROW;或THROW 50000, '自定義消息', 1;RAISERROR:需手動捕獲變量,支援格式化佔位符%s %d,向後兼容TRY...CATCHCATCH塊首檢查@@TRANCOUNT > 0回滾THROW 50000+自定義錯誤號,區分系統錯誤CATCH中吞噬錯誤不重拋(除非刻意靜默)標準化錯誤捕獲模板,讓異常從「黑盒崩潰」變為「可觀測、可追蹤、可恢復」。