在 Transact-SQL 中使用 TRY...CATCH
您可以使用 TRY...CATCH 建構來處理 Transact-SQL 程式碼中的錯誤,這個建構類似於 Microsoft Visual C++ 和 Microsoft Visual C# 語言的例外狀況處理功能。TRY…CATCH 建構由兩部分所組成:TRY 區塊與 CATCH 區塊。在 TRY 區塊內的 Transact-SQL 陳述式中偵測到錯誤狀況時,控制權會傳遞給可處理此錯誤的 CATCH 區塊。
CATCH 區塊處理例外狀況之後,控制權會接著傳遞給 END CATCH 陳述式後面的第一個 Transact-SQL 陳述式。如果 END CATCH 陳述式是預存程序或觸發程序中的最後一個陳述式,則控制權會傳回給叫用此預存程序或觸發程序的程式碼。在 TRY 區塊中,產生錯誤之陳述式後面的 Transact-SQL 陳述式將不會執行。
如果 TRY 區塊內沒有錯誤,控制權會傳遞給緊接在相關聯 END CATCH 陳述式後面的陳述式。如果 END CATCH 陳述式是預存程序或觸發程序中的最後一個陳述式,則控制權會傳遞給叫用此預存程序或觸發程序的陳述式。
TRY 區塊是以 BEGIN TRY 陳述式開始,而以 END TRY 陳述式結束。在 BEGIN TRY 與 END TRY 陳述式之間,可以指定一或多個 Transact-SQL 陳述式。
TRY 區塊後面必須緊接著 CATCH 區塊。CATCH 區塊是以 BEGIN CATCH 陳述式開始,而以 END CATCH 陳述式結束。在 Transact-SQL 中,每一個 TRY 區塊只能與一個 CATCH 區塊相關聯。
使用 TRY...CATCH
使用 TRY...CATCH 建構時,請考慮下列指導方針及建議:
每一個 TRY...CATCH 建構都必須位於單一批次、預存程序或觸發程序中。例如,您無法將 TRY 區塊放置於某個批次,然後將相關聯的 CATCH 區塊放置於另一個批次。下列指令碼會產生錯誤:
BEGIN TRY SELECT * FROM sys.messages WHERE message_id = 21; END TRY GO -- The previous GO breaks the script into two batches, -- generating syntax errors. The script runs if this GO -- is removed. BEGIN CATCH SELECT ERROR_NUMBER() AS ErrorNumber; END CATCH; GO
TRY 區塊後面必須緊接著 CATCH 區塊。
TRY…CATCH 建構可以有巢狀結構。這就表示 TRY…CATCH 建構可以放置於其他 TRY 和 CATCH 區塊內。當巢狀 TRY 區塊內發生錯誤時,程式控制權會傳送給與巢狀 TRY 區塊相關聯的 CATCH 區塊。
若要處理發生在給定的 CATCH 區塊內的錯誤,請在指定的 CATCH 區塊內編寫 TRY......CATCH 區塊。
TRY...CATCH 區塊不會處理嚴重性 20 以上且導致 Database Engine 關閉連接的錯誤。不過,只要連接未關閉,TRY...CATCH 就會處理嚴重性 20 以上的錯誤。
嚴重性 10 以下的錯誤則視為警告或參考用訊息,而且 TRY...CATCH 區塊不會進行處理。
注意事項會結束批次,即使該批次是在 TRY...CATCH 建構的範圍內。這包括當分散式交易失敗時,Microsoft 分散式交易協調器 (MS DTC) 所傳送的注意事項。MS DTC 負責管理分散式交易。
[!附註]
如果分散式交易在 TRY 區塊的範圍內執行,並且發生錯誤,執行動作就會傳送給相關聯的 CATCH 區塊。此時,分散式交易會進入無法認可的狀態。CATCH 區塊內的執行動作可能會被管理分散式交易的 Microsoft 分散式交易協調器中斷。當發生錯誤時,MS DTC 會以非同步方式通知所有參與該分散式交易的伺服器,並結束分散式交易中包含的所有工作。此通知是以注意事項的格式傳送,TRY...CATCH 建構不會處理它,而且批次會結束。當批次執行完成時,Database Engine 會回復任何使用中且無法認可的交易。如果在交易進入無法認可的狀態時沒有傳送任何錯誤訊息,則當批次完成時,就會將錯誤訊息傳送給用戶端應用程式,表示已偵測並回復無法認可的交易。如需分散式交易的詳細資訊,請參閱<分散式交易 (Database Engine)>。
錯誤函數
TRY...CATCH 會使用下列錯誤函數來擷取錯誤資訊:
ERROR_NUMBER() 會傳回錯誤號碼。
ERROR_MESSAGE() 會傳回錯誤訊息的完整文字。這段文字包括提供給可替代參數的值,例如長度、物件名稱或時間。
ERROR_SEVERITY() 傳回錯誤嚴重性。
ERROR_STATE() 會傳回錯誤狀態碼。
ERROR_LINE() 會傳回常式內造成錯誤的行號。
ERROR_PROCEDURE() 會傳回發生錯誤的預存程序或觸發程序的名稱。
您可在 TRY...CATCH 建構之 CATCH 區塊範圍內的任何位置,使用這些函數來擷取錯誤資訊。如果在 CATCH 區塊範圍外呼叫錯誤函數,將傳回 NULL。如果在 CATCH 區塊內執行預存程序,便可在預存程序內參考錯誤函數,而且可以使用這些錯誤函數來擷取錯誤資訊。進行這項動作時,您不需要在每個 CATCH 區塊中重複錯誤處理程式碼。在下列程式碼範例中,TRY 區塊內的 SELECT 陳述式將會產生除以零的錯誤。CATCH 區塊將處理此錯誤,並使用預存程序來傳回錯誤資訊。
USE AdventureWorks;
GO
-- Verify that the stored procedure does not exist.
IF OBJECT_ID ('usp_GetErrorInfo', 'P') IS NOT NULL
DROP PROCEDURE usp_GetErrorInfo;
GO
-- Create a 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 the error retrieval routine.
EXECUTE usp_GetErrorInfo;
END CATCH;
GO
編譯與陳述式層級重新編譯錯誤
如果錯誤是發生在與 TRY...CATCH 建構相同的執行層級,則 TRY...CATCH 建構不會處理下列兩種錯誤類型:
編譯錯誤,例如會阻礙批次執行的語法錯誤。
在陳述式層級重新編譯期間發生的錯誤,例如在編譯之後,因延遲的名稱解析所發生的物件名稱解析錯誤。
當包含 TRY...CATCH 建構的批次、預存程序或觸發程序產生上述其中一種錯誤時,TRY...CATCH 建構不會處理這些錯誤。這些錯誤將傳回給呼叫錯誤產生常式的應用程式或批次。例如,下列程式碼範例顯示會導致語法錯誤的 SELECT 陳述式。如果這個程式碼是在 SQL Server Management Studio 查詢編輯器中執行,就會因為批次無法編譯,所以無法開始執行。錯誤將傳回至查詢編輯器,而 TRY…CATCH 無法捕捉此錯誤。
USE AdventureWorks;
GO
BEGIN TRY
-- This PRINT statement will not run because the batch
-- does not begin execution.
PRINT N'Starting execution';
-- This SELECT statement contains a syntax error that
-- stops the batch from compiling successfully.
SELECT ** FROM HumanResources.Employee;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO
與上述範例中的語法錯誤不同的是,在陳述式層級重新編譯期間發生的錯誤並不會阻礙批次進行編譯,但是只要陳述式重新編譯失敗,就會結束批次。例如,如果批次具有兩個陳述式,而第二個陳述式參考了不存在的資料表,則延遲的名稱解析功能會讓批次編譯成功,並在未將遺漏的資料表繫結到查詢計畫的狀況下便開始執行,而這種狀況需等到該陳述式重新編譯後,才能解決。當批次進行到參考了遺漏資料表的陳述式時,便會停止執行,並傳回錯誤。在發生錯誤的相同執行層級中,TRY...CATCH 建構並不會處理此類型的錯誤。下列範例示範此行為。
USE AdventureWorks;
GO
BEGIN TRY
-- This PRINT statement will run because the error
-- occurs at the SELECT statement.
PRINT N'Starting execution';
-- This SELECT statement will generate an object name
-- resolution error because the table does not exist.
SELECT * FROM NonExistentTable;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO
您可以使用 TRY...CATCH 來處理在編譯或陳述式層級重新編譯期間所發生的錯誤,方法是在 TRY 區塊的個別批次中執行產生錯誤的程式碼。例如,您可以將程式碼放置於預存程序中,或使用 sp_executesql 執行動態 Transact-SQL 陳述式來達成此目標。這樣就可讓 TRY...CATCH 在比錯誤發生時更高的執行層級中捕捉錯誤。例如,下列程式碼範例將顯示一個會產生物件名稱解析錯誤的預存程序。包含 TRY…CATCH 建構的批次是在比預存程序更高的層級執行,因此可以捕捉到在較低層級發生的錯誤。
USE AdventureWorks;
GO
-- Verify that the stored procedure does not already exist.
IF OBJECT_ID ('usp_MyError', 'P') IS NOT NULL
DROP PROCEDURE usp_MyError;
GO
CREATE PROCEDURE usp_MyError
AS
-- This SELECT statement will generate
-- an object name resolution error.
SELECT * FROM NonExistentTable;
GO
BEGIN TRY
-- Run the stored procedure.
EXECUTE usp_MyError;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO
以下為結果集:
ErrorNumber ErrorMessage
----------- ---------------------------------------
208 Invalid object name 'NonExistentTable'.
如需詳細資訊,請參閱<延遲名稱解析及編譯>,以及<執行計畫快取與重複使用>中的「重新編譯執行計畫」一節。
無法認可的交易
在 TRY...CATCH 建構內,交易會進入保持開啟但無法認可的狀態。交易無法執行任何會寫入交易記錄的動作,例如修改資料或嘗試回復到儲存點。不過,在此狀態下,仍會維護交易所取得的鎖定,也會保持連接開啟。要等到發出 ROLLBACK 陳述式,或批次結束並且 Database Engine 自動回復交易時,才會反轉交易的結果。如果在交易進入無法認可的狀態時沒有傳送任何錯誤訊息,則當批次完成時,就會將錯誤訊息傳送給用戶端應用程式,表示已偵測並回復無法認可的交易。
若發生了正常情況下會結束交易的錯誤,TRY 區塊內的該交易將進入無法認可的狀態。例如,資料定義語言 (DDL) 陳述式 (如 CREATE TABLE) 中的大部分錯誤,或當 SET XACT_ABORT 設為 ON 時發生的大部分錯誤,在 TRY 區塊外都會結束交易,但在 TRY 區塊內則會讓交易成為無法認可。
CATCH 區塊中的程式碼應該使用 XACT_STATE 函數來測試交易的狀態。如果工作階段有無法認可的交易,XACT_STATE 將傳回 -1。如果 XACT_STATE 傳回 -1,CATCH 區塊絕不能執行任何會寫入記錄的動作。下列程式碼範例將產生 DDL 陳述式的錯誤,並使用 XACT_STATE 來測試交易的狀態,以便採取最適當的動作。
USE AdventureWorks;
GO
-- Verify that the table does not exist.
IF OBJECT_ID (N'my_books', N'U') IS NOT NULL
DROP TABLE my_books;
GO
-- Create table my_books.
CREATE TABLE my_books
(
Isbn int PRIMARY KEY,
Title NVARCHAR(100)
);
GO
BEGIN TRY
BEGIN TRANSACTION;
-- This statement will generate an error because the
-- column author does not exist in the table.
ALTER TABLE my_books
DROP COLUMN author;
-- If the DDL statement succeeds, commit the transaction.
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() as ErrorNumber,
ERROR_MESSAGE() as ErrorMessage;
-- Test XACT_STATE for 1 or -1.
-- XACT_STATE = 0 means 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 active and valid.
IF (XACT_STATE()) = 1
BEGIN
PRINT
N'The transaction is committable. ' +
'Committing transaction.'
COMMIT TRANSACTION;
END;
END CATCH;
GO
處理死結
TRY...CATCH 可以用來處理死結。CATCH 區塊可以捕捉到 1205 死結犧牲者錯誤,而且可以回復交易,直到執行緒解除鎖定。如需有關死結的詳細資訊,請參閱<死結>。
下列範例將說明如何使用 TRY...CATCH 來處理死結。以下第一個區段會建立一個用來展示死結狀態的資料表,以及一個用來列印錯誤資訊的預存程序。
USE AdventureWorks;
GO
-- Verify that the table does not exist.
IF OBJECT_ID (N'my_sales',N'U') IS NOT NULL
DROP TABLE my_sales;
GO
-- Create and populate the table for deadlock simulation.
CREATE TABLE my_sales
(
Itemid INT PRIMARY KEY,
Sales INT not null
);
GO
INSERT my_sales (itemid, sales) VALUES (1, 1);
INSERT my_sales (itemid, sales) VALUES (2, 1);
GO
-- Verify that the stored procedure for error printing
-- does not exist.
IF OBJECT_ID (N'usp_MyErrorLog',N'P') IS NOT NULL
DROP PROCEDURE usp_MyErrorLog;
GO
-- Create a stored procedure for printing error information.
CREATE PROCEDURE usp_MyErrorLog
AS
PRINT
'Error ' + CONVERT(VARCHAR(50), ERROR_NUMBER()) +
', Severity ' + CONVERT(VARCHAR(5), ERROR_SEVERITY()) +
', State ' + CONVERT(VARCHAR(5), ERROR_STATE()) +
', Line ' + CONVERT(VARCHAR(5), ERROR_LINE());
PRINT
ERROR_MESSAGE();
GO
下列的工作階段 1 及工作階段 2 指令碼,是在兩個個別的 SQL Server Management Studio 連接中同步執行。這兩個工作階段嘗試更新資料表中的同一個資料列。其中一個工作階段在第一次嘗試時便能順利完成更新作業,而另一個工作階段則被選為死結犧牲者。死結犧牲者錯誤會使執行動作跳至 CATCH 區塊,而交易將進入無法認可的狀態。在 CATCH 區塊內,死結犧牲者可以回復交易並再次嘗試更新資料表,直到更新成功或達到重試次數限制,視何者先發生而定。
工作階段 1 |
工作階段 2 |
---|---|
|
|
具有 RAISERROR 的 TRY...CATCH
RAISERROR 可以用在 TRY...CATCH 建構的 TRY 或 CATCH 區塊中,以影響處理錯誤的行為。
在 TRY 區塊內執行而且具有嚴重性 11 到 19 的 RAISERROR 會將控制權傳送到相關聯的 CATCH 區塊。在 CATCH 區塊內執行而且具有嚴重性 11 到 19 的 RAISERROR 會將錯誤傳回進行呼叫的應用程式或批次。如此一來,RAISERROR 可以將導致 CATCH 區塊執行的錯誤詳細資訊傳回給呼叫者。TRY...CATCH 錯誤函數所提供的錯誤資訊可以在 RAISERROR 訊息中擷取,其中包括原始錯誤號碼;不過,RAISERROR 的錯誤號碼必須 >= 50000。
嚴重性 10 以下的 RAISERROR 會傳回參考用訊息給進行呼叫的批次或應用程式,而不叫用 CATCH 區塊。
嚴重性 20 以上的 RAISERROR 會關閉資料庫連接,而不叫用 CATCH 區塊。
下列程式碼範例將說明如何在 CATCH 區塊內使用 RAISERROR,將原始錯誤資訊傳回進行呼叫的應用程式或批次。預存程序 usp_GenerateError 會在 TRY 區塊內執行 DELETE 陳述式,以便產生條件約束違規錯誤。此錯誤會導致執行動作傳送給執行預存程序 usp_RethrowError 之 usp_GenerateError 內的相關聯 CATCH 區塊,以便使用 RAISERROR 來引發條件約束違規錯誤資訊。RAISERROR 所產生的這項錯誤資訊會傳回執行 usp_GenerateError 的呼叫批次,並導致執行動作傳送給呼叫批次內的相關聯 CATCH 區塊。
[!附註]
RAISERROR 只能產生狀態為 1 到 127 的錯誤。因為 Database Engine 可能會引發狀態 0 的錯誤,所以我們建議您在傳送 ERROR_STATE 的值給 RAISERROR 的狀態參數之前,先檢查它傳回的錯誤狀態。
USE AdventureWorks;
GO
-- Verify that stored procedure does not exist.
IF OBJECT_ID (N'usp_RethrowError',N'P') IS NOT NULL
DROP PROCEDURE usp_RethrowError;
GO
-- Create the stored procedure to generate an error using
-- RAISERROR. The original error information is used to
-- construct the msg_str for RAISERROR.
CREATE PROCEDURE usp_RethrowError AS
-- Return if there is no error information to retrieve.
IF ERROR_NUMBER() IS NULL
RETURN;
DECLARE
@ErrorMessage NVARCHAR(4000),
@ErrorNumber INT,
@ErrorSeverity INT,
@ErrorState INT,
@ErrorLine INT,
@ErrorProcedure NVARCHAR(200);
-- Assign variables to error-handling functions that
-- capture information for RAISERROR.
SELECT
@ErrorNumber = ERROR_NUMBER(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorLine = ERROR_LINE(),
@ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
-- Build the message string that will contain original
-- error information.
SELECT @ErrorMessage =
N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' +
'Message: '+ ERROR_MESSAGE();
-- Raise an error: msg_str parameter of RAISERROR will contain
-- the original error information.
RAISERROR
(
@ErrorMessage,
@ErrorSeverity,
1,
@ErrorNumber, -- parameter: original error number.
@ErrorSeverity, -- parameter: original error severity.
@ErrorState, -- parameter: original error state.
@ErrorProcedure, -- parameter: original error procedure name.
@ErrorLine -- parameter: original error line number.
);
GO
-- Verify that stored procedure does not exist.
IF OBJECT_ID (N'usp_GenerateError',N'P') IS NOT NULL
DROP PROCEDURE usp_GenerateError;
GO
-- Create a stored procedure that generates a constraint violation
-- error. The error is caught by the CATCH block where it is
-- raised again by executing usp_RethrowError.
CREATE PROCEDURE usp_GenerateError
AS
BEGIN TRY
-- A FOREIGN KEY constraint exists on the table. This
-- statement will generate a constraint violation error.
DELETE FROM Production.Product
WHERE ProductID = 980;
END TRY
BEGIN CATCH
-- Call the procedure to raise the original error.
EXEC usp_RethrowError;
END CATCH;
GO
-- In the following batch, an error occurs inside
-- usp_GenerateError that invokes the CATCH block in
-- usp_GenerateError. RAISERROR inside this CATCH block
-- generates an error that invokes the outer CATCH
-- block in the calling batch.
BEGIN TRY -- outer TRY
-- Call the procedure to generate an error.
EXECUTE usp_GenerateError;
END TRY
BEGIN CATCH -- Outer CATCH
SELECT
ERROR_NUMBER() as ErrorNumber,
ERROR_MESSAGE() as ErrorMessage;
END CATCH;
GO
變更執行流程
若要變更執行流程,可以在 TRY 區塊或 CATCH 區塊內使用 GOTO。GOTO 也可以用來結束 TRY 區塊或 CATCH 區塊;然而,GOTO 無法用來進入 TRY 區塊或 CATCH 區塊。
AdventureWorks 範例資料庫中的錯誤處理方案
AdventureWorks 範例資料庫包含一個錯誤處理方案,其設計目的是用來記錄 TRY...CATCH 建構之 CATCH 區塊所捕捉到的錯誤相關資訊,而這些資訊可稍後用於查詢或分析。
dbo.ErrorLog 資料表
ErrorLog 資料表所記錄的資訊包括錯誤號碼、錯誤嚴重性、錯誤狀態、發生錯誤之預存程序或觸發程序的名稱、發生錯誤的行號以及錯誤訊息的完整文字。同時也記錄發生錯誤的日期及時間,以及執行了產生錯誤之常式的使用者名稱。在 TRY...CATCH 建構的 CATCH 區塊範圍內執行預存程序 uspLogError 時,即會填入此資料表。如需詳細資訊,請參閱<ErrorLog 資料表 (AdventureWorks)>。
dbo.uspLogError
預存程序 uspLogError 會將導致執行動作傳送給 TRY...CATCH 建構 CATCH 區塊的錯誤相關資訊,記錄在 ErrorLog 資料表。若要讓 uspLogError 能夠將錯誤資訊插入 ErrorLog 資料表,必須存在下列條件:
uspLogError 是在 CATCH 區塊的範圍內執行。
如果目前交易處於無法認可的狀態,就會先回復交易,然後再執行 uspLogError。
uspLogError 的輸出參數 @ErrorLogID,會針對由 uspLogError 插入到 ErrorLog 資料表中的資料列,傳回此資料列的 ErrorLogID。@ErrorLogID 的預設值是 0。下列範例將顯示 uspLogError 的程式碼。如需詳細資訊,請參閱<AdventureWorks 中的預存程序>。
CREATE PROCEDURE [dbo].[uspLogError]
@ErrorLogID [int] = 0 OUTPUT -- Contains the ErrorLogID of the row inserted
-- by uspLogError in the ErrorLog table.
AS
BEGIN
SET NOCOUNT ON;
-- Output parameter value of 0 indicates that error
-- information was not logged.
SET @ErrorLogID = 0;
BEGIN TRY
-- Return if there is no error information to log.
IF ERROR_NUMBER() IS NULL
RETURN;
-- Return if inside an uncommittable transaction.
-- Data insertion/modification is not allowed when
-- a transaction is in an uncommittable state.
IF XACT_STATE() = -1
BEGIN
PRINT 'Cannot log error since the current transaction is in an uncommittable state. '
+ 'Rollback the transaction before executing uspLogError in order to successfully log error information.';
RETURN;
END;
INSERT [dbo].[ErrorLog]
(
[UserName],
[ErrorNumber],
[ErrorSeverity],
[ErrorState],
[ErrorProcedure],
[ErrorLine],
[ErrorMessage]
)
VALUES
(
CONVERT(sysname, CURRENT_USER),
ERROR_NUMBER(),
ERROR_SEVERITY(),
ERROR_STATE(),
ERROR_PROCEDURE(),
ERROR_LINE(),
ERROR_MESSAGE()
);
-- Pass back the ErrorLogID of the row inserted
SELECT @ErrorLogID = @@IDENTITY;
END TRY
BEGIN CATCH
PRINT 'An error occurred in stored procedure uspLogError: ';
EXECUTE [dbo].[uspPrintError];
RETURN -1;
END CATCH
END;
dbo.uspPrintError
預存程序 uspPrintError 會針對將執行動作傳送給 TRY...CATCH 建構之 CATCH 區塊的錯誤,列印其相關資訊。uspPrintError 應該在 CATCH 區塊的範圍內執行,否則此程序會傳回但不會列印任何錯誤資訊。下列範例將顯示 uspPrintError 的程式碼。如需詳細資訊,請參閱<AdventureWorks 中的預存程序>。
CREATE PROCEDURE [dbo].[uspPrintError]
AS
BEGIN
SET NOCOUNT ON;
-- Print error information.
PRINT 'Error ' + CONVERT(varchar(50), ERROR_NUMBER()) +
', Severity ' + CONVERT(varchar(5), ERROR_SEVERITY()) +
', State ' + CONVERT(varchar(5), ERROR_STATE()) +
', Procedure ' + ISNULL(ERROR_PROCEDURE(), '-') +
', Line ' + CONVERT(varchar(5), ERROR_LINE());
PRINT ERROR_MESSAGE();
END;
錯誤處理範例
下列範例將說明 AdventureWorks 錯誤處理方案。TRY 區塊內的程式碼會嘗試刪除 Production.Product 資料表中具有 ProductID 980 的記錄。資料表上的 FOREIGN KEY 條件約束會防止 DELETE 作業順利完成,而且會產生條件約束違規錯誤。此錯誤會導致執行動作傳送給 CATCH 區塊。在 CATCH 區塊內,將發生下列動作:
uspPrintError 列印錯誤資訊。
在回復交易之後,uspLogError 會將錯誤資訊輸入 ErrorLog 資料表,並將插入資料列的 ErrorLogID 傳回給 @ErrorLogID OUTPUT 參數。
USE AdventureWorks;
GO
-- Variable to store ErrorLogID value of the row
-- inserted in the ErrorLog table by uspLogError
DECLARE @ErrorLogID INT;
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 operation succeeds, commit the transaction.
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- Call procedure to print error information.
EXECUTE dbo.uspPrintError;
-- Roll back any active or uncommittable transactions before
-- inserting information in the ErrorLog.
IF XACT_STATE() <> 0
BEGIN
ROLLBACK TRANSACTION;
END
EXECUTE dbo.uspLogError @ErrorLogID = @ErrorLogID OUTPUT;
END CATCH;
-- Retrieve logged error information.
SELECT * FROM dbo.ErrorLog WHERE ErrorLogID = @ErrorLogID;
GO
巢狀錯誤處理範例
下列範例示範使用巢狀 TRY…CATCH 建構。
BEGIN TRY
BEGIN TRY
SELECT CAST('invalid_date' AS datetime)
END TRY
BEGIN CATCH
PRINT 'Inner TRY error number: ' +
CONVERT(varchar,ERROR_NUMBER()) + ' on line: ' +
CONVERT(varchar, ERROR_LINE())
END CATCH
SELECT CAST('invalid_int' AS int)
END TRY
BEGIN CATCH
PRINT 'Outer TRY error mumber: ' + CONVERT(varchar,ERROR_NUMBER())+
' on line: ' + CONVERT(varchar, ERROR_LINE())
END CATCH
以下為結果集:
Inner TRY error number: 241 on line: 3
Outer TRY error number: 245 on line: 9
請參閱