SQL捕获异常
Transact-SQL 代碼中的錯誤可使用 TRY…CATCH 構造處理,此功能類似于 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 語句。
CATCH 塊必須緊跟 TRY 塊。CATCH 塊以 BEGIN CATCH 語句開頭,以 END CATCH 語句結尾。在 Transact-SQL 中,每個 TRY 塊僅與一個 CATCH 塊相關聯。
使用 TRY...CATCH
使用 TRY...CATCH 構造時,請遵循下列規則和建議:
-
每個 TRY...CATCH 構造都必須位于一個批處理、存儲過程或觸發器中。例如,不能將 TRY 塊放置在一個批處理中而將關聯的 CATCH 塊放置在另一個批處理中。下面的腳本將生成一個錯誤:
BEGIN TRYSELECT *FROM sys.messagesWHERE 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 CATCHSELECT ERROR_NUMBER() AS ErrorNumber; END CATCH; GO -
CATCH 塊必須緊跟 TRY 塊。
-
TRY…CATCH 構造可以是嵌套式的。這意味著可以將 TRY…CATCH 構造放置在其他 TRY 塊和 CATCH 塊內。當嵌套的 TRY 塊中出現錯誤時,程序控制將傳遞到與嵌套的 TRY 塊關聯的 CATCH 塊。
-
若要處理給定的 CATCH 塊中出現的錯誤,請在指定的 CATCH 塊中編寫 TRY...CATCH 塊。
-
TRY...CATCH 塊不處理導致數據庫引擎關閉連接的嚴重性為 20 或更高的錯誤。但是,只要連接不關閉,TRY...CATCH 就會處理嚴重性為 20 或更高的錯誤。
-
嚴重性為 10 或更低的錯誤被視為警告或信息性消息,TRY...CATCH 塊不處理此類錯誤。
-
即使批處理位于 TRY...CATCH 構造的作用域內,關注消息仍將終止該批處理。分布式事務失敗時,Microsoft 分布式事務處理協調器 (MS DTC) 將發送關注消息。MS DTC 用于管理分布式事務。
注意 如 果在 TRY 塊的作用域內執行分布式事務且發生錯誤,執行將傳遞到關聯的 CATCH 塊。分布式事務進入不可提交狀態。CATCH 塊中的執行可能由管理分布式事務的 Microsoft 分布式事務處理協調器中斷。發生錯誤時,MS DTC 將異步通知參與分布式事務的所有服務器,并終止分布式事務中涉及的所有任務。此類通知以關注消息的形式發送(TRY...CATCH 構造不處理此類通知),批處理將被終止。當批處理完成運行時,數據庫引擎將回滾所有不可提交的活動事務。如果事務進入不可提交狀態時未發送錯誤消息,則當 批處理完成時,將向客戶端應用程序發送錯誤消息,該消息指示檢測到或回滾了一個不可提交的事務。有關分布式事務的詳細信息,請參閱 分布式事務(數據庫引擎) 。
錯誤函數
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 AdventureWorks2008R2; GO-- Verify that the stored procedure does not exist. IF OBJECT_ID ('usp_GetErrorInfo', 'P') IS NOT NULLDROP PROCEDURE usp_GetErrorInfo; GO-- Create a procedure to retrieve error information. CREATE PROCEDURE usp_GetErrorInfo ASSELECT ERROR_NUMBER() AS ErrorNumber,ERROR_SEVERITY() AS ErrorSeverity,ERROR_STATE() as ErrorState,ERROR_PROCEDURE() as ErrorProcedure,ERROR_LINE() as ErrorLine,ERROR_MESSAGE() as ErrorMessage; GOBEGIN 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 AdventureWorks2008R2; GOBEGIN 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 CATCHSELECT ERROR_NUMBER() AS ErrorNumber,ERROR_MESSAGE() AS ErrorMessage; END CATCH; GO與上述示例中的語法錯誤不同,語句級重新編譯過程中發生的錯誤不會阻礙批處理進行編譯,但是一旦語句重新編譯失敗,它會立即終止批處理。例如,如果 批處理含有兩條語句并且第二條語句引用的表不存在,則延遲的名稱解析會使該批處理成功進行編譯并開始執行(無需將缺少的表綁定到查詢計劃),直到重新編譯 該語句為止。批處理到達引用缺失表的語句時將停止運行,并返回一個錯誤。在發生錯誤的執行級別,TRY...CATCH 構造將不處理此類錯誤。以下示例對此行為進行了說明。
USE AdventureWorks2008R2; GOBEGIN 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 CATCHSELECT ERROR_NUMBER() AS ErrorNumber,ERROR_MESSAGE() AS ErrorMessage; END CATCH; GO通過執行 TRY 塊內單獨批處理中的錯誤生成代碼,可以使用 TRY...CATCH 來處理編譯或語句級重新編譯過程中發生的錯誤。例如,這可以通過在存儲過程中放置代碼或使用 sp_executesql 執行動態 Transact-SQL 語句來實現。這使 TRY...CATCH 能夠在比錯誤發生的執行級別更高的執行級別捕獲錯誤。例如,下面的代碼顯示一個生成對象名解析錯誤的存儲過程。包含 TRY...CATCH 構造的批處理在比存儲過程更高的級別執行,并捕獲在更低級別發生的錯誤。
USE AdventureWorks2008R2; GO-- Verify that the stored procedure does not already exist. IF OBJECT_ID ('usp_MyError', 'P') IS NOT NULLDROP PROCEDURE usp_MyError; GOCREATE PROCEDURE usp_MyError AS-- This SELECT statement will generate-- an object name resolution error.SELECT * FROM NonExistentTable; GOBEGIN TRY-- Run the stored procedure.EXECUTE usp_MyError; END TRY BEGIN CATCHSELECT ERROR_NUMBER() AS ErrorNumber,ERROR_MESSAGE() AS ErrorMessage; END CATCH; GO下面是結果集:
ErrorNumber ErrorMessage ----------- --------------------------------------- 208 Invalid object name 'NonExistentTable'.有關詳細信息,請參閱 延遲名稱解析和編譯 以及 執行計劃的緩存和重新使用 中的“重新編譯執行計劃”一節。
不可提交的事務
在 TRY...CATCH 構造中,事務可以進入一種狀態:事務保持打開但無法提交。事務無法執行寫事務日志的任何操作,例如修改數據或嘗試回滾到保存點。但是,在此狀態下,事務獲 取的鎖將被維護,并且連接也保持打開。發出 ROLLBACK 語句之前,或批處理結束并且數據庫引擎自動回滾事務之前,不會逆轉事務效果。如果事務進入不可提交狀態時未發送錯誤信息,則當批處理完成時,將向客戶端應 用程序發送錯誤消息,該消息指示檢測到或回滾了一個不可提交的事務。
發生錯誤時,事務在 TRY 塊內進入無法提交狀態,否則此錯誤將終止該事務。例如,數據定義語言 (DDL) 語句(如 CREATE TABLE)中的大多數錯誤或 SET XACT_ABORT 設置為 ON 時出現的大多數錯誤都在 TRY 塊外終止事務,而在 TRY 塊內使事務無法提交。
CATCH 塊中的代碼可以通過使用 XACT_STATE 函數來測試事務的狀態。如果會話中包含無法提交的事務,XACT_STATE 將返回 -1。如果 XACT_STATE 返回 -1,則 CATCH 塊將不能執行寫日志的任何操作。下面的代碼示例生成 DDL 語句錯誤,并使用 XACT_STATE 測試事務的狀態,以便執行最合適的操作。
USE AdventureWorks2008R2; GO-- Verify that the table does not exist. IF OBJECT_ID (N'my_books', N'U') IS NOT NULLDROP TABLE my_books; GO-- Create table my_books. CREATE TABLE my_books(Isbn int PRIMARY KEY,Title NVARCHAR(100)); GOBEGIN TRYBEGIN TRANSACTION;-- This statement will generate an error because the -- column author does not exist in the table.ALTER TABLE my_booksDROP COLUMN author;-- If the DDL statement succeeds, commit the transaction.COMMIT TRANSACTION; END TRY BEGIN CATCHSELECTERROR_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()) = -1BEGINPRINTN'The transaction is in an uncommittable state. ' +'Rolling back transaction.'ROLLBACK TRANSACTION;END;-- Test whether the transaction is active and valid.IF (XACT_STATE()) = 1BEGINPRINTN'The transaction is committable. ' +'Committing transaction.'COMMIT TRANSACTION; END; END CATCH; GO處理死鎖
TRY...CATCH 可用于處理死鎖。CATCH 塊可以捕獲 1205 死鎖犧牲品錯誤,并且事務可以回滾,直至線程解鎖。有關死鎖的詳細信息,請參閱 死鎖 。
下面的示例顯示如何使用 TRY...CATCH 處理死鎖。第一部分創建用于說明死鎖狀態的表和用于打印錯誤信息的存儲過程。
USE AdventureWorks2008R2; GO-- Verify that the table does not exist. IF OBJECT_ID (N'my_sales',N'U') IS NOT NULLDROP TABLE my_sales; GO-- Create and populate the table for deadlock simulation. CREATE TABLE my_sales (Itemid INT PRIMARY KEY,Sales INT not null); GOINSERT 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 NULLDROP PROCEDURE usp_MyErrorLog; GO-- Create a stored procedure for printing error information. CREATE PROCEDURE usp_MyErrorLog ASPRINT '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 塊中,死鎖犧牲品會回滾事務并重試更新此表,直到更新成功或達到了重試限制(以先發生者為準)。
| USE AdventureWorks2008R2; GO-- Declare and set variable -- to track number of retries -- to try before exiting. DECLARE @retry INT; SET @retry = 5;-- Keep trying to update -- table if this task is -- selected as the deadlock -- victim. WHILE (@retry > 0) BEGINBEGIN TRYBEGIN TRANSACTION;UPDATE my_salesSET sales = sales + 1WHERE itemid = 1;WAITFOR DELAY '00:00:13';UPDATE my_salesSET sales = sales + 1WHERE itemid = 2;SET @retry = 0;COMMIT TRANSACTION;END TRYBEGIN CATCH -- Check error number.-- If deadlock victim error,-- then reduce retry count-- for next update retry. -- If some other error-- occurred, then exit-- retry WHILE loop.IF (ERROR_NUMBER() = 1205)SET @retry = @retry - 1;ELSESET @retry = -1;-- Print error information.EXECUTE usp_MyErrorLog;IF XACT_STATE() <> 0ROLLBACK TRANSACTION;END CATCH; END; -- End WHILE loop. GO | USE AdventureWorks2008R2; GO-- Declare and set variable -- to track number of retries -- to try before exiting. DECLARE @retry INT; SET @retry = 5;--Keep trying to update -- table if this task is -- selected as the deadlock -- victim. WHILE (@retry > 0) BEGINBEGIN TRYBEGIN TRANSACTION;UPDATE my_salesSET sales = sales + 1WHERE itemid = 2;WAITFOR DELAY '00:00:07';UPDATE my_salesSET sales = sales + 1WHERE itemid = 1;SET @retry = 0;COMMIT TRANSACTION;END TRYBEGIN CATCH -- Check error number.-- If deadlock victim error,-- then reduce retry count-- for next update retry. -- If some other error-- occurred, then exit-- retry WHILE loop.IF (ERROR_NUMBER() = 1205)SET @retry = @retry - 1;ELSESET @retry = -1;-- Print error information.EXECUTE usp_MyErrorLog;IF XACT_STATE() <> 0ROLLBACK TRANSACTION;END CATCH; END; -- End WHILE loop. GO |
使用 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_GenerateError 內關聯的 CATCH 塊,存儲過程 usp_RethrowError 在此塊內執行以使用 RAISERROR 生成違反約束錯誤。RAISERROR 生成的此錯誤將返回到調用批處理(usp_GenerateError 在其中執行)并使執行傳遞到調用批處理中關聯的 CATCH 塊。
| RAISERROR 僅能生成狀態從 1 到 127 的錯誤。由于數據庫引擎可能引發狀態為 0 的錯誤,因此,建議您先檢查由 ERROR_STATE 返回的錯誤狀態,然后將它作為一個值傳遞給狀態參數 RAISERROR。 |
更改執行流
轉載于:https://www.cnblogs.com/sxmny/archive/2012/11/01/2749286.html
總結
- 上一篇: 埋线多少钱一次啊?
- 下一篇: SendMessage和PostMess