TRY...CATCH (Transact-SQL)

適用于:SQL Server (Azure SQL Database Azure SQL 受控執行個體 Azure Synapse Analytics Analytics Platform System ( PDW) 的所有支援) 版本

實作 Transact-SQL 的錯誤處理,其類似于 visual C# Microsoft 和 Microsoft 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 與更早版本的 Transact-SQL 語法,請參閱舊版文件

引數

sql_statement
這是任何 Transact-SQL 語句。

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

備註

TRY...CATCH 建構會擷取嚴重性高於 10 而未關閉資料庫連線的所有執行錯誤。

TRY 區塊後面必須緊接著相關聯的 CATCH 區塊。 在 END TRY 與 BEGIN CATCH 陳述式之間包含任何其他陳述式,將會產生語法錯誤。

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

如果 TRY 區塊所含括的程式碼沒有錯誤,當 TRY 區塊中的最後一個陳述式完成執行時,控制權會傳給緊接在相關聯的 END CATCH 陳述式之後的陳述式。

如果 TRY 區塊所含括的程式碼發生錯誤,控制權會傳給相關聯的 CATCH 區塊中的第一個陳述式。 當 CATCH 區塊中的程式碼完成時,控制權會傳給緊接在 END CATCH 陳述式之後的陳述式。

注意

如果 END CATCH 陳述式是預存程序或觸發程序中的最後一個陳述式,控制權便會傳回呼叫預存程序或引發觸發程序的陳述式。

CATCH 區塊擷取的錯誤不會傳回發出呼叫的應用程式。 如果有任何錯誤資訊必須傳回應用程式,CATCH 區塊中的程式碼便必須利用 SELECT 結果集或 RAISERROR 和 PRINT 陳述式之類的機制來執行這個動作。

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 建構,錯誤會將控制權傳回給與包含 EXECUTE 陳述式之 TRY 區塊相關聯的 CATCH 區塊。

  • 如果預存程序包含 TRY...CATCH 建構,錯誤會將控制權傳送給預存程序中的 CATCH 區塊。 當 CATCH 區塊程式碼完成時,控制權會傳回給緊接在呼叫預存程序的 EXECUTE 陳述式之後的陳述式。

GOTO 陳述式無法用來進入 TRY 或 CATCH 區塊。 GOTO 陳述式可用來跳到相同 TRY 或 CATCH 區塊內的標籤,或離開 TRY 或 CATCH 區塊。

在使用者定義函數內,無法使用 TRY...CATCH 建構。

擷取錯誤資訊

在 CATCH 區塊的範圍內,下列系統函數可用來取得造成執行 CATCH 區塊之錯誤的相關資訊:

如果是在 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 Database Engine 工作處理。 如果發生嚴重性為 20 或以上的錯誤,且資料庫連線並未中斷,TRY...CATCH 會處理這個錯誤。

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

  • 系統管理員利用 KILL 陳述式來結束工作階段。

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

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

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

  • 物件名稱解析錯誤

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

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

下列範例顯示 SELECT 陳述式所產生的物件名稱解析錯誤,是在預存程序內執行相同的 TRY...CATCH 陳述式時,由 CATCH 區塊來擷取,而不是由 SELECT 建構來擷取。

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 值。 當批次完成時,Database Engine 會回復任何作用中無法認可的交易。 如果在交易進入無法認可的狀態時沒有傳送任何錯誤訊息,則當批次完成時,就會將錯誤訊息傳送給用戶端應用程式。 這表示偵測到無法認可的交易,並且需要回復它。

如需有關不可認可交易和XACT_STATE函式的詳細資訊,請參閱 XACT_STATE (Transact-SQL)

範例

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  

D. 使用 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_MESSAGE() AS ErrorMessage;  
END CATCH;  
GO  

另請參閱

THROW (Transact-SQL)
Database Engine 錯誤嚴重性
ERROR_LINE (Transact-SQL)
ERROR_MESSAGE (Transact-SQL)
ERROR_NUMBER (Transact-SQL)
ERROR_PROCEDURE (Transact-SQL)
ERROR_SEVERITY (Transact-SQL)
ERROR_STATE (Transact-SQL)
RAISERROR (Transact-SQL)
@@ERROR (Transact-SQL)
GOTO (Transact-SQL)
BEGIN...END (Transact-SQL)
XACT_STATE (Transact-SQL)
SET XACT_ABORT (Transact-SQL)