The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION

若是你的存儲過程或其餘腳本出現下面這個錯誤,通常是由於ROLLBACK TRANSACTION在邏輯上缺乏匹配的BEGIN TRANSACTION或者沒有開始一個事務(也有可能此事務已經提交),可是你作了事務回滾操做(ROLLBACK TRANSACTION),不然就可能出現這種錯誤。數據庫

 

Msg 3903, Level 16, State 1, Line 22app

The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.spa

 

出現這種錯誤有不少種可能性,下面咱們來經過一些案例來簡單介紹一下這個錯誤,這些案例都是一些特殊案例的簡化版本。code

 

案例1:orm

 

CREATE PROCEDURE PRC_EXC
AS
BEGIN
    SELECT 1/0  --僅僅模擬存儲過程出現異常。
END;
GO 
 
CREATE PROCEDURE PRC_TEST
AS
BEGIN
 
    BEGIN TRY
        BEGIN TRAN TT
 
            UPDATE dbo.TEST SET NAME='k3' WHERE object_id =9;
 
        COMMIT TRAN TT;
 
        EXEC dbo.PRC_EXC
 
    END TRY
    BEGIN CATCH
        ROLLBACK TRAN TT;
    END CATCH
END

 

若是你執行存儲過程PRC_TEST,以下所示,由於執行存儲過程dbo.PRC_EXC時遇到異常被捕獲,此時在BEGIN CATCH部分執行ROLLBACK TRAN TT,可是實際上,此事務已經提交,數據庫根本沒有這樣一個事務,而後你又要回滾事務,因此出錯。可能讓人好奇的是爲何存儲過程dbo.PRC_EXC不放在事務裏面,這裏僅僅是簡單模擬生產環境的一個案例,正確的作法應該將dbo.PRC_EXC放入事務當中,或者將dbo.PRC_EXC放入另一個BEGIN TRY ... END TRY裏面去。blog

 

 

clip_image001

 

 

若是要在捕獲一個事務裏面出現異常的正確的作法以下所示,我的更傾向於第二種寫法。事務

 

BEGIN TRANSACTION;
 
BEGIN TRY
 
   ...................
   ...................
   --執行全部業務邏輯後,最後提交
   COMMIT;
 
END TRY
 
BEGIN CATCH
 
   --if an exception occurs execute your rollback, also test that you have had some successful transactions
   IF @@TRANCOUNT > 0 ROLLBACK
 
 
 
END CATCH
 
 
 
 
BEGIN TRY
 
   BEGIN TRANSACTION;
   ....................
   ....................
   --執行全部業務邏輯後,最後提交
   COMMIT;
 
END TRY
 
BEGIN CATCH
 
   --if an exception occurs execute your rollback, also test that you have had some successful transactions
   IF @@TRANCOUNT > 0 ROLLBACK
 
END CATCH

 

 

案例2:ip

 

CREATE PROCEDURE PRC_TEST2
AS 
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRANSACTION
 
    UPDATE dbo.TEST SET NAME='k3' WHERE object_id =9;    --這裏用簡單的UPDATE替換複雜的業務邏輯。
 
    IF @@Error != 0 GOTO ERROR_HANDLER
 
     UPDATE dbo.TEST SET NAME='k3' WHERE object_id =15; --這裏用簡單的UPDATE替換複雜的業務邏輯。
    
    IF @@Error != 0 GOTO ERROR_HANDLER
 
COMMIT TRANSACTION
    
ERROR_HANDLER: ROLLBACK TRANSACTION
SET NOCOUNT OFF
RETURN 0
GO

 

 

上面錯誤的緣由,在於沒有異常或錯誤時,事務提交後,這一句ERROR_HANDLER: ROLLBACK TRANSACTION老是會被執行,邏輯上已經沒有事務了。因此正確的作法,事務提交後,直接RETURN,避免正常狀況下執行ERROR_HANDLER: ROLLBACK TRANSACTION,或者將回滾邏輯放到IF條件以後,不要用GOTO這種寫法.ci

 

 

正確的SQL:get

 

ALTER PROCEDURE PRC_TEST2
AS 
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRANSACTION
 
    UPDATE dbo.TEST SET NAME='k3' WHERE object_id =9;    --這裏用簡單的UPDATE替換複雜的業務邏輯。
 
    IF @@Error != 0 GOTO ERROR_HANDLER
 
     UPDATE dbo.TEST SET NAME='k3' WHERE object_id =15;   --這裏用簡單的UPDATE替換複雜的業務邏輯。
IF @@Error != 0 GOTO ERROR_HANDLER
 
COMMIT TRANSACTION
SET NOCOUNT OFF
RETURN 0;
    
ERROR_HANDLER: ROLLBACK TRANSACTION
SET NOCOUNT OFF
RETURN 0
GO

 

這裏來一個簡單的演示,你能夠體會一下,所謂的BEGIN TRAN與ROLLBACK  TRANSACTION並非指數量匹對,而是邏輯上事務回滾前,必須有一個未提交的事務。

 

SELECT * INTO test FROM sys.objects
 
SELECT  @@TRANCOUNT;--值爲0
BEGIN TRAN
UPDATE  TEST SET     name = 'kkk' WHERE   object_id =7;
SELECT  @@TRANCOUNT;--值爲1,
COMMIT TRAN
 
ROLLBACK TRAN;  --事務其實已經結束,忽然來一個回滾事務,沒有匹配的BEGIN TRAN,因此出現報錯"The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION."
 

 

 

案例3:

 

下面這種錯誤,純屬菜鳥級別犯的錯誤或粗枝大葉所致。

 

CREATE PROCEDURE PRC_TEST4
AS 
SET NOCOUNT ON
 
BEGIN
 
BEGIN TRY
 
    UPDATE dbo.TEST SET NAME='k3' WHERE object_id =9;    --這裏用簡單的UPDATE替換複雜的業務邏輯。
 
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION
END CATCH
END;

 

總結:

實際案例中,若是存儲過程裏面有複雜的業務邏輯,尤爲出現嵌套調用存儲過程的時候,特別是多層嵌套時,這種問題排查起來也至關麻煩。因此儘可能少用嵌套調用存儲過程。簡化業務邏輯!另外,出現這種錯誤時,須要仔細檢查代碼邏輯才能找出這些出錯的地方,彷佛也沒有其它更好的方法。

相關文章
相關標籤/搜索