分享方式:


TRY...CATCH (Transact-SQL)

適用於:SQL Server Azure SQL 資料庫 Azure SQL 受控執行個體 Azure Synapse Analytics Analytics Platform System (PDW) Microsoft Fabric 的 SQL 端點分析 Microsoft Fabric 的倉儲

實作 Transact-SQL 的錯誤處理,類似於 C# 和 Visual C++ 語言中的例外狀況處理。 Transact-SQL 語句的群組可以封入區塊中 TRY 。 如果區塊中 TRY 發生錯誤,控件通常會傳遞至區塊中所 CATCH 封入的另一組語句。

Transact-SQL 語法慣例

Syntax

BEGIN TRY
    { sql_statement | statement_block }
END TRY
BEGIN CATCH
    [ { sql_statement | statement_block } ]
END CATCH
[ ; ]

注意

若要檢視 SQL Server 2014 (12.x) 和更早版本的 Transact-SQL 語法,請參閱舊版文件

引數

sql_statement

任何 Transact-SQL 語句。

statement_block

批次中或以區塊括住 BEGIN...END 的任何 Transact-SQL 語句群組。

備註

TRY...CATCH 構會攔截所有嚴重性高於 10 但未關閉資料庫連線的執行錯誤。

TRY區塊後面必須緊接著相關聯的CATCH區塊。 包含和 BEGIN CATCH 語句之間的END TRY任何其他語句會產生語法錯誤。

TRY...CATCH建構無法跨越多個批次。 TRY...CATCH建構無法跨越 Transact-SQL 語句的多個區塊。 例如,建構無法跨越 Transact-SQL 語句的兩BEGIN...ENDTRY...CATCH區塊,而且無法跨越IF...ELSE建構。

如果區塊中最後一個 TRY 語句 TRY 完成時,區塊中的程式代碼中沒有任何錯誤,控件就會在相關聯的 END CATCH 語句之後立即傳遞至 語句。

如果區塊中 TRY 含有錯誤,控件會傳遞至相關聯 CATCH 區塊中的第一個語句。 當區塊中的 CATCH 程式代碼完成時,控件會緊接在語句之後 END CATCH 傳遞至 語句。

注意

END CATCH如果語句是預存程式或觸發程式中的最後一個語句,控件會傳回至呼叫預存程式或引發觸發程式的語句。

區塊所 CATCH 截獲的錯誤不會傳回給呼叫的應用程式。 如果錯誤資訊的任何部分必須傳回給應用程式,區塊中的CATCH程式代碼必須使用結果集或 RAISERRORPRINT 語句等SELECT機制來執行此動作。

TRY...CATCH 建構可以是巢狀結構。 TRY區塊或CATCH區塊可以包含巢狀TRY...CATCH建構。 例如, CATCH 區塊可以包含內嵌 TRY...CATCH 建構來處理程序代碼所遇到的 CATCH 錯誤。

區塊中 CATCH 遇到的錯誤會被視為在其他地方產生的錯誤。 CATCH如果區塊包含巢狀TRY...CATCH建構,巢狀TRY區塊中的任何錯誤都會將控制權傳遞至巢狀CATCH區塊。 如果沒有巢狀 TRY...CATCH 建構,錯誤會傳回給呼叫端。

TRY...CATCH 建構會從預存程式或區塊中的 TRY 程式代碼所執行的觸發程式攔截未處理的錯誤。 或者,預存程式或觸發程式可以包含自己的 TRY...CATCH 建構,以處理其程式代碼所產生的錯誤。 例如,當 TRY 區塊執行預存程式,並在預存程式中發生錯誤時,可以使用下列方式處理錯誤:

  • 如果預存程式未包含自己的TRY...CATCH建構,則錯誤會將控件CATCH傳回與包含 EXECUTE 語句之TRY區塊相關聯的區塊。

  • 如果預存程式包含 TRY...CATCH 建構,錯誤會將控件傳輸至 CATCH 預存程式中的 區塊。 當區塊程序 CATCH 代碼完成時,控件會緊接在呼叫預存程式的語句之後 EXECUTE 傳回語句。

GOTO 語句無法用來輸入 TRYCATCH 區塊。 GOTO 語句可用來跳至相同 TRYCATCH 區塊內的標籤,或離開 TRYCATCH 區塊。

TRY...CATCH 構不能用於使用者定義函式。

擷取錯誤資訊

在區塊的範圍內 CATCH ,下列系統函式可用來取得導致 CATCH 區塊執行之錯誤的相關信息:

函式 描述
ERROR_NUMBER 傳回錯誤的數目。
ERROR_SEVERITY 傳回嚴重性。
ERROR_STATE 傳回錯誤狀態號碼。
ERROR_PROCEDURE 傳回發生錯誤的預存程式或觸發程式名稱。
ERROR_LINE 傳回造成錯誤的例程內的行號。
ERROR_MESSAGE 傳回錯誤訊息的完整文字。 文字包括提供給任何可替代參數的值,例如,長度、物件名稱或次數。

這些函式會在區塊範圍CATCH之外呼叫它們時傳回 NULL 。 您可以從區塊範圍內 CATCH 的任何位置使用這些函式來擷取錯誤資訊。 例如,下列指令碼顯示包含錯誤處理函數的預存程序。 在 CATCH建構的 TRY...CATCH區塊中,會呼叫預存程序,並傳回錯誤的相關資訊。

-- Verify that the stored procedure does not already exist.
IF OBJECT_ID('usp_GetErrorInfo', 'P') IS NOT NULL
    DROP PROCEDURE usp_GetErrorInfo;
GO

-- Create procedure to retrieve error information.
CREATE PROCEDURE usp_GetErrorInfo
AS
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;
GO

BEGIN TRY
    -- Generate divide-by-zero error.
    SELECT 1 / 0;
END TRY

BEGIN CATCH
    -- Execute error retrieval routine.
    EXECUTE usp_GetErrorInfo;
END CATCH;

函式ERROR_*也會在CATCH原生編譯預存程式內的 區塊中運作。

TRY 未受影響的錯誤...CATCH 建構

TRY...CATCH 建構不會設陷下列條件:

  • 嚴重性為 10 或以下的警告或參考訊息。

  • 嚴重性為 20 或以上的錯誤,會停止工作階段的 SQL Server 資料庫引擎工作處理。 如果發生嚴重性為 20 或更新版本且資料庫連線未中斷的錯誤, TRY...CATCH 請處理錯誤。

  • 用戶端中斷要求或中斷用戶端連接之類的注意事項。

  • 當系統管理員使用 KILL 語句結束會話時。

當發生於與建構相同的執行TRY...CATCH層級時,區塊不會處理CATCH下列類型的錯誤:

  • 造成無法執行批次的編譯錯誤,如語法錯誤。

  • 在陳述式層級重新編譯期間發生的錯誤,例如在編譯之後,因延遲的名稱解析所發生的物件名稱解析錯誤。

  • 物件名稱解析錯誤

這些錯誤會傳回執行批次、預存程序或觸發程序的層級。

如果在編譯或語句層級重新編譯期間發生錯誤,且執行層級較低(例如,在區塊內TRY執行sp_executesql或使用者定義預存程式時),則錯誤會在低於建構的層級TRY...CATCH發生,且將由相關聯的CATCH區塊處理。

下列範例示範 語句所產生的SELECT物件名稱解析錯誤不會由 TRY...CATCH 建構攔截,但在預存程式內執行相同SELECT語句時,區塊會攔截CATCH

BEGIN TRY
    -- Table does not exist; object name resolution
    -- error not caught.
    SELECT *
    FROM NonexistentTable;
END TRY

BEGIN CATCH
    SELECT ERROR_NUMBER() AS ErrorNumber,
        ERROR_MESSAGE() AS ErrorMessage;
END CATCH

錯誤不會攔截,而且控制權會從建構傳出 TRY...CATCH 至下一個較高層級。

SELECT 預存程式內執行 語句會導致錯誤發生在低於 區塊的 TRY 層級。 錯誤是由 TRY...CATCH 建構所處理。

-- Verify that the stored procedure does not exist.
IF OBJECT_ID(N'usp_ExampleProc', N'P') IS NOT NULL
    DROP PROCEDURE usp_ExampleProc;
GO

-- Create a stored procedure that will cause an
-- object resolution error.
CREATE PROCEDURE usp_ExampleProc
AS
SELECT *
FROM NonexistentTable;
GO

BEGIN TRY
    EXECUTE usp_ExampleProc;
END TRY

BEGIN CATCH
    SELECT ERROR_NUMBER() AS ErrorNumber,
        ERROR_MESSAGE() AS ErrorMessage;
END CATCH;

無法認可的交易和XACT_STATE

如果區塊中 TRY 產生的錯誤導致目前交易的狀態失效,交易就會分類為無法認可的交易。 通常結束區塊外部 TRY 交易的錯誤會導致交易在區塊內 TRY 發生錯誤時進入無法認可的狀態。 無法認可的交易只能執行讀取作業或 ROLLBACK TRANSACTION。 交易無法執行任何會產生寫入作業或 COMMIT TRANSACTION的 Transact-SQL 語句。 如果交易已分類為無法認可的交易,則函 XACT_STATE 式會傳回的值 -1 。 當批次完成後,資料庫引擎會回復所有使用中的無法認可交易。 如果交易進入無法認可狀態時未傳送任何錯誤訊息,當批次完成時,就會將錯誤訊息傳送至客戶端應用程式。 這表示偵測到無法認可的交易,並且需要回復它。

如需無法認可交易和函 XACT_STATE 式的詳細資訊,請參閱 XACT_STATE

範例

A. 使用 TRY...CATCH

下列範例顯示將會產生除以零之錯誤的 SELECT 陳述式。 這個錯誤會使執行動作跳到相關聯的 CATCH 區塊。

BEGIN TRY
    -- Generate a divide-by-zero error.
    SELECT 1 / 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;
END CATCH;
GO

B. 使用 TRY...交易中的 CATCH

下列範例顯示 TRY...CATCH 區塊在交易內運作的方式。 TRY 區塊內的陳述式產生條件約束違規錯誤。

BEGIN TRANSACTION;

BEGIN TRY
    -- Generate a constraint violation error.
    DELETE
    FROM Production.Product
    WHERE ProductID = 980;
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;
END CATCH;

IF @@TRANCOUNT > 0
    COMMIT TRANSACTION;
GO

C. 使用 TRY...CATCH 與 XACT_STATE

下列範例顯示如何利用 TRY...CATCH 建構來處理交易內所發生的錯誤。 XACT_STATE 函數會判斷是否應該認可或回復交易。 在此範例中,SET XACT_ABORTON。 當發生條件約束違規錯誤時,會使交易成為無法認可。

-- Check to see whether this stored procedure exists.
IF OBJECT_ID(N'usp_GetErrorInfo', N'P') IS NOT NULL
    DROP PROCEDURE usp_GetErrorInfo;
GO

-- Create procedure to retrieve error information.
CREATE PROCEDURE usp_GetErrorInfo
AS
SELECT ERROR_NUMBER() AS ErrorNumber,
    ERROR_SEVERITY() AS ErrorSeverity,
    ERROR_STATE() AS ErrorState,
    ERROR_LINE() AS ErrorLine,
    ERROR_PROCEDURE() AS ErrorProcedure,
    ERROR_MESSAGE() AS ErrorMessage;
GO

-- SET XACT_ABORT ON will cause the transaction to be uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;

    -- A FOREIGN KEY constraint exists on this table. This
    -- statement will generate a constraint violation error.
    DELETE
    FROM Production.Product
    WHERE ProductID = 980;

    -- If the DELETE statement succeeds, commit the transaction.
    COMMIT TRANSACTION;
END TRY

BEGIN CATCH
    -- Execute error retrieval routine.
    EXECUTE usp_GetErrorInfo;

    -- Test XACT_STATE:
    -- If 1, the transaction is committable.
    -- If -1, the transaction is uncommittable and should
    --     be rolled back.
    -- XACT_STATE = 0 means that there is no transaction and
    --     a commit or rollback operation would generate an error.
    -- Test whether the transaction is uncommittable.
    IF (XACT_STATE()) = -1
    BEGIN
        PRINT N'The transaction is in an uncommittable state. Rolling back transaction.'

        ROLLBACK TRANSACTION;
    END;

    -- Test whether the transaction is committable.
    -- You may want to commit a transaction in a catch block if you want to commit changes to statements that ran prior to the error.
    IF (XACT_STATE()) = 1
    BEGIN
        PRINT N'The transaction is committable. Committing transaction.'

        COMMIT TRANSACTION;
    END;
END CATCH;
GO