TRY...CATCH (Transact-SQL)
實作類似於 Microsoft Visual C# 與 Microsoft Visual C++ 語言中之例外狀況處理的 Transact-SQL 錯誤處理。您可以將 Transact-SQL 陳述式群組含括在 TRY 區塊內。如果 TRY 區塊內發生錯誤,就會將控制權傳給含括在 CATCH 區塊內的另一個陳述式群組。
語法
BEGIN TRY
{ sql_statement | statement_block }
END TRY
BEGIN CATCH
[ { sql_statement | statement_block } ]
END CATCH
[ ; ]
引數
sql_statement
這是任何 Transact-SQL 陳述式。statement_block
在批次或 BEGIN…END 區塊中的任何 Transact-SQL 陳述式群組。
備註
TRY…CATCH 建構會擷取嚴重性高於 10 而未關閉資料庫連接的所有執行錯誤。
TRY 區塊後面必須緊接著相關聯的 CATCH 區塊。在 END TRY 與 BEGIN CATCH 陳述式之間包含任何其他陳述式,將會產生語法錯誤。
TRY…CATCH 建構不能跨越多個批次。TRY…CATCH 建構不能跨越多個 Transact-SQL 陳述式區塊。例如,TRY…CATCH 建構不能跨越 Transact-SQL 陳述式的兩個 BEGIN…END 區塊,也不能跨越 IF…ELSE 建構。
如果 TRY 區塊所含括的程式碼沒有錯誤,當 TRY 區塊中的最後一個陳述式完成執行時,控制權會傳給緊接在相關聯的 END CATCH 陳述式之後的陳述式。如果 TRY 區塊所含括的程式碼發生錯誤,控制權會傳給相關聯的 CATCH 區塊中的第一個陳述式。如果 END CATCH 陳述式是預存程序或觸發程序中的最後一個陳述式,控制權便會傳回呼叫預存程序或引發觸發程序的陳述式。
當 CATCH 區塊中的程式碼完成時,控制權會傳給緊接在 END CATCH 陳述式之後的陳述式。CATCH 區塊擷取的錯誤不會傳回發出呼叫的應用程式。如果有任何錯誤資訊必須傳回應用程式,CATCH 區塊中的程式碼便必須利用 SELECT 結果集或 RAISERROR 和 PRINT 陳述式之類的機制來執行這個動作。如需有關如何使用含有 TRY…CATCH 之 RAISERROR 的詳細資訊,請參閱<在 Transact-SQL 中使用 TRY...CATCH>。
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 區塊之錯誤的相關資訊:
ERROR_NUMBER() 會傳回錯誤碼。
ERROR_SEVERITY() 會傳回嚴重性。
ERROR_STATE() 會傳回錯誤狀態碼。
ERROR_PROCEDURE() 會傳回發生錯誤的預存程序或觸發程序的名稱。
ERROR_LINE() 會傳回常式內造成錯誤的行號。
ERROR_MESSAGE() 會傳回錯誤訊息的完整文字。文字包括提供給任何可替代參數的值,例如,長度、物件名稱或次數。
如果是在 CATCH 區塊範圍之外呼叫這些函數,它們會傳回 NULL。這些函數可以從 CATCH 區塊範圍內的任何位置擷取錯誤資訊。例如,下列指令碼顯示包含錯誤處理函數的預存程序。在 TRY…CATCH 建構的 CATCH 區塊中,會呼叫預存程序,並傳回錯誤的相關資訊。
USE AdventureWorks2008R2;
GO
-- 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;
不受 TRY…CATCH 建構影響的錯誤
TRY…CATCH 建構不會擷取下列狀況:
嚴重性為 10 或以下的警告或參考訊息。
嚴重性為 20 或以上的錯誤,它們會停止工作階段的 SQL Server Database Engine 工作處理。如果發生嚴重性為 20 或以上的錯誤,但資料庫連接並未中斷,TRY…CATCH 仍會處理這個錯誤。
用戶端中斷要求或中斷用戶端連接之類的注意事項。
系統管理員利用 KILL 陳述式來結束工作階段。
當 TRY…CATCH 建構的相同執行層級發生下列錯誤類型時,CATCH 區塊不會處理這些錯誤:
造成無法執行批次的編譯錯誤,如語法錯誤。
在陳述式層級重新編譯期間發生的錯誤,例如在編譯之後,因延遲的名稱解析所發生的物件名稱解析錯誤。
這些錯誤會傳回執行批次、預存程序或觸發程序的層級。
如果在 TRY 區塊內,在編譯或陳述式層級重新編譯期間,較低的執行層級發生錯誤 (例如,執行 sp_executesql 或使用者定義預存程序時),發生錯誤的層級會低於 TRY…CATCH 建構,並由相關聯的 CATCH 區塊來處理。如需詳細資訊,請參閱<在 Transact-SQL 中使用 TRY...CATCH>。
下列範例顯示 SELECT 陳述式所產生的物件名稱解析錯誤,是在預存程序內執行相同的 SELECT 陳述式時,由 CATCH 區塊來擷取,而不是由 TRY…CATCH 建構來擷取。
USE AdventureWorks2008R2;
GO
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 函數的詳細資訊,請參閱<在 Transact-SQL 中使用 TRY...CATCH>和<XACT_STATE (Transact-SQL)>。
範例
A. 使用 TRY…CATCH
下列範例顯示將會產生除以零的錯誤之 SELECT 陳述式。這個錯誤會使執行動作跳到相關聯的 CATCH 區塊。
USE AdventureWorks2008R2;
GO
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 區塊內的陳述式產生條件約束違規錯誤。
USE AdventureWorks2008R2;
GO
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. 搭配 XACT_STATE 使用 TRY…CATCH
下列範例顯示如何利用 TRY…CATCH 建構來處理交易內所發生的錯誤。XACT_STATE 函數會判斷是否應該認可或回復交易。在此範例中,SET XACT_ABORT 是 ON。當發生條件約束違規錯誤時,會使交易成為無法認可。
USE AdventureWorks2008R2;
GO
-- 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.
IF (XACT_STATE()) = 1
BEGIN
PRINT
N'The transaction is committable.' +
'Committing transaction.'
COMMIT TRANSACTION;
END;
END CATCH;
GO
請參閱