在 Transact-SQL 中使用 TRY...CATCH

在 Transact-SQL 中使用 TRY...CATCH

(注:本文來自於 http://msdn.microsoft.com/zh-cn/library/ms179296.aspx)sql

 

            SQL Server 2008 R2     

        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 塊放置在另外一個批處理中。下面的腳本將生成一個錯誤:ide

     
    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
    
  • CATCH 塊必須緊跟 TRY 塊。函數

  • TRY…CATCH 構造能夠是嵌套式的。這意味着能夠將 TRY…CATCH 構造放置在其餘 TRY 塊和 CATCH 塊內。當嵌套的 TRY 塊中出現錯誤時,程序控制將傳遞到與嵌套的 TRY 塊關聯的 CATCH 塊。oop

  • 若要處理給定的 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 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 AdventureWorks2008R2;
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 AdventureWorks2008R2;
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 塊內單獨批處理中的錯誤生成代碼,可使用 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 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 語句以前,或批處理結束而且數據庫引擎自動回滾事務以前,不會逆轉事務效果。若是事務進入不可提交狀態時未發送錯誤信息,則當批處理完成時,將向客戶端應用程序發送錯誤消息,該消息指示檢測到或回滾了一個不可提交的事務。

發生錯誤時,事務在 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 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 AdventureWorks2008R2;
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

 
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)
BEGIN
    BEGIN TRY
        BEGIN TRANSACTION;
    
        UPDATE my_sales
        SET sales = sales + 1
        WHERE itemid = 1;

        WAITFOR DELAY '00:00:13';
    
        UPDATE my_sales
        SET sales = sales + 1
        WHERE itemid = 2;

        SET @retry = 0;

        COMMIT TRANSACTION;
    END TRY
    BEGIN 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;
        ELSE
            SET @retry = -1;

        -- Print error information.
        EXECUTE usp_MyErrorLog;
  
        IF XACT_STATE() <> 0
            ROLLBACK 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)
BEGIN
    BEGIN TRY
       BEGIN TRANSACTION;
    
        UPDATE my_sales
        SET sales = sales + 1
        WHERE itemid = 2;

        WAITFOR DELAY '00:00:07';
    
        UPDATE my_sales
        SET sales = sales + 1
        WHERE itemid = 1;

        SET @retry = 0;

        COMMIT TRANSACTION;
    END TRY
    BEGIN 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;
        ELSE
            SET @retry = -1;

        -- Print error information.
        EXECUTE usp_MyErrorLog;
  
        IF XACT_STATE() <> 0
            ROLLBACK 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_GenerateErrorTRY 塊內執行 DELETE 語句,該語句生成違反約束錯誤。此錯誤使執行傳遞到 usp_GenerateError 內關聯的 CATCH 塊,存儲過程 usp_RethrowError 在此塊內執行以使用 RAISERROR 生成違反約束錯誤。RAISERROR 生成的此錯誤將返回到調用批處理(usp_GenerateError 在其中執行)並使執行傳遞到調用批處理中關聯的 CATCH 塊。

注意注意

RAISERROR 僅能生成狀態從 1 到 127 的錯誤。因爲數據庫引擎可能引起狀態爲 0 的錯誤,所以,建議您先檢查由 ERROR_STATE 返回的錯誤狀態,而後將它做爲一個值傳遞給狀態參數 RAISERROR。

 
USE AdventureWorks2008R2;
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 塊。

AdventureWorks2008R2 示例數據庫中包含一個錯誤處理解決方案,該方案設計爲記錄有關 TRY...CATCH 構造的 CATCH 塊捕獲的錯誤的信息,以便於之後進行查詢或分析。

dbo.ErrorLog 表

ErrorLog 表記錄有關錯誤號、錯誤嚴重性、錯誤狀態、發生錯誤的存儲過程或觸發器的名稱、發生錯誤的行號以及錯誤消息的完整文本的信息。它還記錄發生錯誤的日期和時間以及執行錯誤生成例程的用戶名。存儲過程 uspLogError 在 TRY...CATCH 構造的 CATCH 塊的做用域中執行時,將填充此表。

dbo.uspLogError

存儲過程 uspLogError 在 ErrorLog 表中記錄有關使執行傳遞到 TRY...CATCH 構造的 CATCH 塊的錯誤的錯誤信息。爲使 uspLogError 將錯誤信息插入到 ErrorLog 表中,必須知足下列條件:

  • uspLogError 在 CATCH 塊的做用域內執行。

  • 若是當前事務處於沒法提交狀態,則在執行 uspLogError 以前回滾該事務。

uspLogError 的輸出參數 @ErrorLogID 返回由 uspLogError 插入到 ErrorLog 表中的行的 ErrorLogID。@ErrorLogID 的默認值爲 0。下面的示例顯示 uspLogError 的代碼。

 
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 的代碼。

 
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;

錯誤處理示例

下面的示例說明 AdventureWorks2008R2 錯誤處理解決方案。TRY 塊內的代碼嘗試刪除 Production.Product 表中有關 ProductID 980 的記錄。此表的 FOREIGN KEY 約束將阻止成功完成 DELETE 語句,並生成一個違反約束錯誤。此錯誤將使執行傳遞到 CATCH 塊。在 CATCH 塊內,將發生下列操做:

  • uspPrintError 輸出錯誤信息。

  • 事務回滾後,uspLogError 將在 ErrorLog 表中輸入錯誤信息,並將插入行的 ErrorLogID 返回給 @ErrorLogID OUTPUT 參數。

 
USE AdventureWorks2008R2;
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

 

 

(注:本文來自於 http://msdn.microsoft.com/zh-cn/library/ms179296.aspx)

相關文章
相關標籤/搜索