天天看點

如何在sqlserver 的函數或存儲過程中抛出異常

raiserror 的作用: raiserror 是用于抛出一個錯誤。[ 以下資料來源于sql server 2005的幫助 ]

其文法如下:

RAISERROR ( { msg_id | msg_str | @local_variable }        
            { ,severity ,state }        
            [ ,argument [ ,...n ] ] 
          )       
   [ WITH option [ ,...n ] ]

簡要說明一下:

第一個參數:{ msg_id | msg_str | @local_variable }
      msg_id:表示可以是一個sys.messages表中定義的消息代号;
              使用 sp_addmessage 存儲在 sys.messages 目錄視圖中的使用者定義錯誤消息号。
              使用者定義錯誤消息的錯誤号應當大于 50000。

     msg_str:表示也可以是一個使用者定義消息,該錯誤消息最長可以有 2047 個字元;
             (如果是常量,請使用N'xxxx',因為是nvarchar的)
              當指定 msg_str 時,RAISERROR 将引發一個錯誤号為 5000 的錯誤消息。

     @local_variable:表示也可以是按照 msg_str 方式的格式化字元串變量。

第二個參數:severity
            使用者定義的與該消息關聯的嚴重級别。(這個很重要)
            任何使用者都可以指定 0 到 18 之間的嚴重級别。
            [0,10]的閉區間内,不會跳到catch;
            如果是[11,19],則跳到catch;
            如果[20,無窮),則直接終止資料庫連接配接;

第三個參數:state
            如果在多個位置引發相同的使用者定義錯誤,
            則針對每個位置使用唯一的狀态号有助于找到引發錯誤的代碼段。

            介于 1 至 127 之間的任意整數。(state 預設值為1)
            當state 值為 0 或大于 127 時會生成錯誤!

第四個參數:argument
            用于代替 msg_str 或對應于 msg_id 的消息中的定義的變量的參數。

第五個參數:option
            錯誤的自定義選項,可以是下表中的任一值:
            LOG :在錯誤日志和應用程式日志中記錄錯誤;
            NOWAIT:将消息立即發送給用戶端;
            SETERROR:将 @@ERROR 值和 ERROR_NUMBER 值設定為 msg_id 或 50000;

  [SQL]代碼示例

--示例1DECLARE @raiseErrorCode nvarchar(50)
SET @raiseErrorCode = CONVERT(nvarchar(50), YOUR UNIQUEIDENTIFIER KEY)
RAISERROR('%s INVALID ID. There is no record in table',16,1, @raiseErrorCode)

--示例2RAISERROR (
             N'This is message %s %d.', -- Message text,
             10,                        -- Severity,
             1,                         -- State,
             N'number',                 -- First argument.
             5                          -- Second argument.
          ); 
-- The message text returned is: This is message number 5.
GO

--示例3RAISERROR (N'<<%*.*s>>', -- Message text.
           10,           -- Severity,
           1,            -- State,
           7,            -- First argument used for width.
           3,            -- Second argument used for precision.
           N'abcde');    -- Third argument supplies the string.
-- The message text returned is: <<    abc>>.
GO

--示例4RAISERROR (N'<<%7.3s>>', -- Message text.
           10,           -- Severity,
           1,            -- State,
           N'abcde');    -- First argument supplies the string.
-- The message text returned is: <<    abc>>.
GO

--示例5    
--A. 從 CATCH 塊傳回錯誤消息
以下代碼示例顯示如何在 TRY 塊中使用 RAISERROR 使執行跳至關聯的 CATCH 塊中。
它還顯示如何使用 RAISERROR 傳回有關調用 CATCH 塊的錯誤的資訊。

BEGIN TRY
    RAISERROR ('Error raised in TRY block.', -- Message text.
                16, -- Severity.
                1 -- State.
               );
END TRY
BEGIN CATCH
    DECLARE @ErrorMessage NVARCHAR(4000);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;

    SELECT 
        @ErrorMessage = ERROR_MESSAGE(),
        @ErrorSeverity = ERROR_SEVERITY(),
        @ErrorState = ERROR_STATE();

    RAISERROR (@ErrorMessage,  -- Message text.
               @ErrorSeverity, -- Severity.
               @ErrorState     -- State.
               );
END CATCH;

--示例6
--B. 在 sys.messages 中建立即席消息
以下示例顯示如何引發 sys.messages 目錄視圖中存儲的消息。
該消息通過 sp_addmessage 系統存儲過程,以消息号50005添加到 sys.messages 目錄視圖中。

sp_addmessage @msgnum = 50005,
               @severity = 10,
               @msgtext = N'<<%7.3s>>';
GO

RAISERROR (50005, -- Message id.
           10,    -- Severity,
           1,     -- State,
           N'abcde'); -- First argument supplies the string.
-- The message text returned is: <<    abc>>.
GO

sp_dropmessage @msgnum = 50005;
GO

--示例7
--C. 使用局部變量提供消息文本
以下代碼示例顯示如何使用局部變量為 RAISERROR 語句提供消息文本。sp_addmessage @msgnum = 50005,
              @severity = 10,
              @msgtext = N'<<%7.3s>>';
GO

RAISERROR (50005, -- Message id.
           10,    -- Severity,
           1,     -- State,
           N'abcde'); -- First argument supplies the string.
-- The message text returned is: <<    abc>>.
GO

sp_dropmessage @msgnum = 50005;
GO      

同時在存儲過程中将事務和try…catch聯合使用

在存儲過程中使用事務時,如果沒有try…catch語句,那麼當set xact_abort on時,如果有錯誤發生,在批處理語句結束後,系統會自動復原所有的sql操作。當set xact_abort off時,如果有錯誤發生,在批處理語句結束後,系統會執行所有沒有發生錯誤的語句,發生錯誤的語句将不會被執行。

在存儲過程中使用事務時,如果存在try…catch語句塊,那麼當捕獲到錯誤時,需要在catch語句塊中手動進行Rollback操作,否則系統會給用戶端傳遞一條錯誤資訊。如果在存儲過程開始處将set xact_abort on,那麼當有錯誤發生時,系統會将目前事務置為不可送出狀态,即會将xact_state()置為-1,此時隻可以對事務進行Rollback操作,不可進行送出(commit)操作,那麼我們在catch語句塊中就可以根據xact_state()的值來判斷是否有事務處于不可送出狀态,如果有則可以進行rollback操作了。如果在存儲過程開始處将set xact_abort off,那麼當有錯誤發生時,系統不會講xact_state()置為-1,那麼我們在catch塊中就不可以根據該函數值來判斷是否需要進行rollback了,但是我們可以根據@@Trancount全局變量來判斷,如果在catch塊中判斷出@@Trancount數值大于0,代表還有未送出的事務,既然進入catch語句塊了,那麼還存在未送出的事務,該事務應該是需要rollback的,但是這種方法在某些情況下可能判斷的不準确。推薦的方法還是将set xact_abort on,然後在catch中判斷xact_state()的值來判斷是否需要Rollback操作。

下面我們來看看兩個例子:

一.使用Set xact_abort on

 Create  proc  myProcedure

As

    begin

       set xact_abort on;

       begin try

           begin tran

              insert into TestStu values('Terry','boy',23);

              insert into TestStu values('Mary','girl',21);

           commit tran

       end try

       begin catch

            -- 在此可以使用xact_state()來判斷是否有不可送出的事務,不可送出的事務

            -- 表示在事務内部發生錯誤了。Xact_state()有三種值: - 1 .事務不可送出;

            -- 1 .事務可送出; 0 .表示沒有事務,此時commit或者rollback會報錯。

            if  xact_state() =- 1

              rollback tran;

       end  catch

end

二.使用Set xact_abort off

Create proc myProcedure As begin set xact_abort off; begin try begin tran insert into TestStu values( ' Terry ' , ' boy ' , 23 ); insert into TestStu values( ' Mary ' , ' girl ' , 21 ); commit tran end try begin catch -- 在此不可以使用xact_state來判斷是否有不可送出的事務 -- 隻可以使用@@Trancount來判斷是否有還未送出的事務,未送出的事務未必 -- 就是不可送出的事務,是以使用@@TranCount > 0後就RollBack是不準确的 if @@TranCount > 0 rollback tran; end catch end

另外,對于@@Trancount需要說明的是,begin tran 語句将 @@Trancount加 1。Rollback tran将 @@Trancount遞減      

存儲過程中使用事務的簡單文法

在存儲過程中使用事務時非常重要的,使用資料可以保持資料的關聯完整性,在Sql server存儲過程中使用事務也很簡單,用一個例子來說明它的文法格式:

 Create Procedure  MyProcedure

    (   @Param1       nvarchar(10),

       @param2    nvarchar(10)    )

    AS

       Begin

Set NOCOUNT ON;

           Set XACT_ABORT ON;

           Begin  Tran

              Delete from       table1 where name=’abc’;

              Insert into       table2 values(value1,value2,value3);

           Commit Tran

       End 

說明:1 、使用存儲過程執行事物,需要開啟XACT_ABORT參數(預設值為Off),将該參數設定為On,表示當執行事務時,如果出錯,會将transcation設定為uncommittable狀态,那麼在語句塊批處理結束後将復原所有操作;如果該參數設定為Off,表示當執行事務時,如果出錯,出錯的語句将不會執行,其他正确的操作繼續執行。

2、當SET NOCOUNT 為 ON 時,不傳回計數(計數表示受 Transact-SQL 語句影響的行數,例如在Sql server查詢分析器中執行一個delete操作後,下方視窗會提示(3)Rows Affected)。當 SET NOCOUNT 為 OFF 時,傳回計數,我們應該在存儲過程的頭部加上SET NOCOUNT ON 這樣的話,在退出存儲過程的時候加上 SET NOCOUNT OFF這樣的話,以達到優化存儲過程的目的。

 二、事務内設定儲存點

  使用者可以在事務内設定儲存點或标記。儲存點定義如果有條件地取消事務的一部分,事務可以傳回的位置。如果将事務復原到儲存點,則必須(如果需要,使用更多的 Transact-SQL 語句和 COMMIT TRANSACTION 語句)繼續完成事務,或者必須(通過将事務復原到其起始點)完全取消事務。若要取消整個事務,請使用 ROLLBACK TRANSACTION transaction_name 格式。這将撤消事務的所有語句和過程。如:

代碼

Create Procedure MyProcedure AS Begin Set NOCOUNT ON; Set XACT_ABORT ON; begin tran ok -- 開始一個事務OK delete from rxqz where qz = ' rx015 ' -- 删除資料 save tran bcd -- 儲存一個事務點命名為bcd update sz set name = ' 李麗s ' where name = ' 李麗 ' -- 修改資料 if @@error <> 0 -- 判斷修改資料有沒有出錯 begin -- 如果出錯 rollback tran bcd -- 復原事務到BCD 的還原點 commit tran ok -- 送出事務 end else -- 沒有出錯 commit tran ok -- 送出事務 End

 說明:1、@@error判斷是否有錯誤,為0表示沒有錯誤,但是對那種重大錯誤無法捕捉,而且@@error隻能前一句sql語句生效。 

三、存儲過程使用try…catch捕獲錯誤

  在存儲過程中可以使用try…catch語句來捕獲錯誤,如下:  

Create Procedure MyProcedure ( @Param1 nvarchar( 10 ), @param2 nvarchar( 10 ) ) AS Begin Set NOCOUNT ON; Begin try Delete from table1 where name = ’abc’; Insert into table2 values(value1,value2,value3); End try Begin Catch SELECT ERROR_NUMBER() AS ErrorNumber, ERROR_MESSAGE() AS ErrorMessage; End Catch End

 說明:1、捕獲錯誤的函數有很多,如下:

              ERROR_NUMBER() 傳回錯誤号。

    ERROR_SEVERITY() 傳回嚴重性。

    ERROR_STATE() 傳回錯誤狀态号。

    ERROR_PROCEDURE() 傳回出現錯誤的存儲過程或觸發器的名稱。

    ERROR_LINE() 傳回導緻錯誤的例程中的行号。

    ERROR_MESSAGE() 傳回錯誤消息的完整文本。該文本可包括任何可替換參數所提供的值,如長度、對象名或時間。

    2、有些錯誤,如sql語句中的表名稱輸入錯誤,這是資料庫引擎無法解析這個表名稱時,所發生的錯誤在目前的try…catch語句中無法捕獲,必須由外層調用該存儲過程的地方使用 try…catch來進行捕獲。

繼續閱讀