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 建構可以有巢狀結構。 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 AdventureWorks2012;
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 區塊來處理。
下列範例顯示 SELECT 陳述式所產生的物件名稱解析錯誤,是在預存程序內執行相同的 SELECT 陳述式時,由 CATCH 區塊來擷取,而不是由 TRY…CATCH 建構來擷取。
USE AdventureWorks2012;
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 函數的詳細資訊,請參閱<XACT_STATE (Transact-SQL)>。
範例
A.使用 TRY…CATCH
下列範例顯示將會產生除以零的錯誤之 SELECT 陳述式。 這個錯誤會使執行動作跳到相關聯的 CATCH 區塊。
USE AdventureWorks2012;
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 AdventureWorks2012;
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.使用 TRY…CATCH 搭配 XACT_STATE
下列範例顯示如何利用 TRY…CATCH 建構來處理交易內所發生的錯誤。 XACT_STATE 函數會判斷是否應該認可或回復交易。 在此範例中,SET XACT_ABORT 是 ON。 當發生條件約束違規錯誤時,會使交易成為無法認可。
USE AdventureWorks2012;
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
請參閱
參考
ERROR_PROCEDURE (Transact-SQL)