首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

SQL Server 2005 引入 TRY...CATCH 錯誤處理:告別 @@ERROR 時代

     編輯:MySQL 資料庫相關知識

前言:結構化錯誤處理終於來了

SQL Server 2005 前,錯誤處理靠 @@ERROR 全域變數檢查每條語句後狀態,程式碼冗長、易漏檢、難維護。TRY...CATCH 區塊帶來類 C#/Java 的結構化異常處理,大幅提升可讀性與健壯性。

基本語法

BEGIN TRY
    -- 可能發生錯誤的 T-SQL 陳述式
    INSERT INTO Accounts (UserName, Balance) VALUES ('user1', 1000);
    UPDATE Accounts SET Balance = Balance - 500 WHERE UserName = 'user1';
    -- 成功路徑
    SELECT 0 AS ResultCode;  -- 或 RETURN 0 (在預存程序中)
END TRY
BEGIN CATCH
    -- 錯誤處理路徑
    SELECT 
        ERROR_NUMBER()   AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE()    AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE()     AS ErrorLine,
        ERROR_MESSAGE()  AS ErrorMessage;
    -- 回滾交易 (若在交易中)
    IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
    SELECT -1 AS ResultCode;  -- 或 RETURN -1
END CATCH;

核心錯誤函數 (僅在 CATCH 區塊內有效)

函數回傳值
ERROR_NUMBER()錯誤號 (如 2627 唯一鍵衝突、547 FK 衝突)
ERROR_SEVERITY()嚴重性等級 (10 以下資訊性、11-16 使用者可修正、17-25 系統錯誤)
ERROR_STATE()錯誤狀態碼 (同一錯誤號不同發生點)
ERROR_PROCEDURE()發生錯誤的預存程序/觸發器名稱 (NULL 為動態批次)
ERROR_LINE()發生錯誤的行號
ERROR_MESSAGE()完整錯誤訊息文字 (含參數替換)

實戰範例:預存程序中的交易與錯誤處理

CREATE PROCEDURE dbo.TransferFunds
    @FromAccount INT, @ToAccount INT, @Amount MONEY
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
        BEGIN TRANSACTION;
        
        UPDATE Accounts SET Balance -= @Amount WHERE AccountID = @FromAccount;
        IF @@ROWCOUNT = 0 THROW 50001, '轉出帳戶不存在', 1;
        
        UPDATE Accounts SET Balance += @Amount WHERE AccountID = @ToAccount;
        IF @@ROWCOUNT = 0 THROW 50002, '轉入帳戶不存在', 1;
        
        INSERT INTO TransferLog (FromAcc, ToAcc, Amount, TxnDate)
        VALUES (@FromAccount, @ToAccount, @Amount, GETDATE());
        
        COMMIT TRANSACTION;
        SELECT 0 AS ResultCode, 'Success' AS Msg;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
        
        -- 記錄錯誤至日誌表
        INSERT INTO ErrorLog (ProcName, ErrorNum, ErrorMsg, ErrorTime, Params)
        VALUES (ERROR_PROCEDURE(), ERROR_NUMBER(), ERROR_MESSAGE(), GETDATE(),
                CONCAT('@From=',@FromAccount,',@To=',@ToAccount,',@Amt=',@Amount));
        
        -- 回傳給呼叫端
        SELECT -1 AS ResultCode, ERROR_MESSAGE() AS Msg;
    END CATCH;
END;

THROW vs RAISERROR (SQL Server 2012+ 新增 THROW)

特性RAISERRORTHROW
語法RAISERROR(msg, severity, state, ...)THROW error_number, message, state
重新拋出需保存錯誤資訊再 RAISERRORTHROW; 在 CATCH 中直接重拋原錯誤
嚴重性可指定 0-25固定 16 (使用者錯誤)
語句結束不終止批次 (需 RETURN)終止當前批次/預存程序

建議:SQL Server 2012+ 統一使用 THROW;2005/2008/R2 使用 RAISERROR + RETURN。

限制與注意事項

  • 不捕獲:編譯錯誤 (語法錯誤)、物件不存在導致的延遲編譯錯誤、嚴重性 > 20 的系統錯誤 (如磁碟滿、記憶體不足)
  • XACT_ABORT:設定 SET XACT_ABORT ON 可確保嚴重錯誤 (如死鎖 1205) 自動回滾交易並進入 CATCH
  • 巢狀 TRY...CATCH:支援巢狀,內層 CATCH 可用 THROW 外拋
  • 效能:TRY 區塊零開銷,CATCH 僅在錯誤時執行,比 @@ERROR 檢查更高效

遷移舊碼清單

  1. 找出所有 IF @@ERROR <> 0 檢查點
  2. 將邏輯包裹於 BEGIN TRY...END TRY
  3. 錯誤處理移至 BEGIN CATCH...END CATCH
  4. 替換 RAISERROR 為 THROW (版本允許時)
  5. 加入 SET XACT_ABORT ON 於預存程序開頭
  6. 建立統一 ErrorLog 表與記錄預存程序

總結

TRY...CATCH 是 SQL Server 2005 最重要的 T-SQL 增強之一。它讓資料庫程式碼具備**現代程式語言級的錯誤處理能力**:集中處理、資訊豐富、交易安全、易於除錯。請在所有新開發與重構專案中全面採用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved