2012-03-15 91 views
12

我有一個存儲過程,需要設置一個保存點,以便它可以,在某些情況下,撤消它所做的一切,並返回一個錯誤代碼給調用者,或接受/提交併將成功返回給調用者。但是,無論呼叫者是否已經開始交易,我都需要它。該文件在這個問題上非常混亂。這是我認爲會起作用的,但我不確定所有的後果。SAVE TRANSACTION vs BEGIN TRANSACTION(SQL Server)如何嵌套事務很好

東西是 - 這個Stored Procedure (SP)被別人調用。所以我不知道他們是否已經開始交易...即使我要求用戶開始交易以使用我的SP,我仍然有關於正確使用Save Points的問題...

我的SP將測試交易是否正在進行,如果沒有,請從BEGIN TRANSACTION開始。如果一個交易已經在進行中,它將會創建一個保存點,並且保存這個事實,這就是我所做的。

然後如果我必須回滾我的更改,如果我早些時候做了BEGIN TRANSACTION,那麼我會ROLLBACK TRANSACTION。如果我做了保存點,那麼我會ROLLBACK TRANSACTION MySavePointName。這種情況似乎很好。

這裏我有點困惑 - 如果我想保持我所做的工作,如果我開始交易,我將執行COMMIT TRANSACTION。但是,如果我創建了一個保存點?我試過COMMIT TRANSACTION MySavePointName,但隨後呼叫者嘗試提交其交易,並得到一個錯誤:

The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

所以我不知道那麼 - 保存點可以回滾(即作品:ROLLBACK TRANSACTION MySavePointName將不會回滾調用者的交易)。但也許人們從不需要「承諾」呢?它只停留在那裏,以防需要回滾到原始事務,但在原始事務被提交(或回滾)後會消失?

如果有一種「更好」的方式來「嵌套」一個交易,請說明一下。我還沒有想出如何嵌套BEGIN TRANSACTION,但只有回滾或提交我的內部交易。似乎ROLLBACK將始終回滾到最高交易,而COMMIT只是遞減@@trancount

+0

你發現可能是值得張貼作爲一個答案。 – 2012-03-16 08:49:22

+0

@Andriy好的,那就是我所做的 - 刪除我的編輯並將其用作答案。謝謝。 – 2012-03-16 21:13:22

回答

19

我相信我現在已經想通了這一切了,所以我會回答我的問題...

我甚至在博客中我發現,如果你在http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx

所以我想SP更多詳情開始有這樣的事情,對是否存在沒有啓動新的事務,而是用一個保存點,如果一個已經在進行中:

DECLARE @startingTranCount int 
SET @startingTranCount = @@TRANCOUNT 

IF @startingTranCount > 0 
    SAVE TRANSACTION mySavePointName 
ELSE 
    BEGIN TRANSACTION 
-- … 

然後,當準備提交更改,你只需要提交如果我們自己開始交易:

IF @startingTranCount = 0 
    COMMIT TRANSACTION 

最後,回滾只是更改至今:

-- Roll back changes... 
IF @startingTranCount > 0 
    ROLLBACK TRANSACTION MySavePointName 
ELSE 
    ROLLBACK TRANSACTION 
+0

有任何理由在'save transaction'後面沒有啓動一個新的嵌套事務? – sotn 2017-05-05 10:51:29

+0

下面是關於嵌套事務的文章的鏈接。如果提交嵌套事務,它會降低嵌套事務計數但不提交任何內容。因此,如果外部事務已提交,則所有內容如果回滾,則會回滾所有內容,包括您的「已提交」內容。如果嵌套事務執行回滾,它將自動回滾和外部事務。如果您只想回滾自己的交易而不影響您已經處於的任何交易,請使用此處描述的技術。 – 2017-05-05 16:12:08

+0

這是關於嵌套事務的文章:https://technet.microsoft.com/en-us/library/ms189336%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396 – 2017-05-05 16:12:50

10

擴展Brian B's answer

這可確保保存點名稱是唯一的,並使用SQL Server 2012的新TRY/CATCH/THROW功能。

DECLARE @mark CHAR(32) = replace(newid(), '-', ''); 
DECLARE @trans INT = @@TRANCOUNT; 

IF @trans = 0 
    BEGIN TRANSACTION @mark; 
ELSE 
    SAVE TRANSACTION @mark; 

BEGIN TRY 
    -- do work here 

    IF @trans = 0 
     COMMIT TRANSACTION @mark; 
END TRY 
BEGIN CATCH 
    IF xact_state() = 1 OR (@trans = 0 AND xact_state() <> 0) ROLLBACK TRANSACTION @mark; 
    THROW; 
END CATCH 
+0

我還沒回來在這篇文章中有一段時間,但喜歡你可以在下一個查詢中重複使用的模板。不錯的提升。 – 2015-10-06 02:03:21

+0

我在MSDN上看到了在try塊內放置begin transaction語句的建議。這樣做會導致一些問題或完全安全嗎? – 2017-02-08 21:33:40

+0

我對catch塊中的if語句感到困惑。爲什麼在xact state = -1時回滾事務?它不是<> - 1? – 2017-08-14 09:56:36

2

我已經使用這種類型的事務管理器在我的存儲過程:

CREATE PROCEDURE Ardi_Sample_Test 
     @InputCandidateID INT 
    AS 
     DECLARE @TranCounter INT; 
     SET @TranCounter = @@TRANCOUNT; 
     IF @TranCounter > 0 
      SAVE TRANSACTION ProcedureSave; 
     ELSE 
      BEGIN TRANSACTION; 
     BEGIN TRY 

      /* 
      <Your Code> 
      */ 

      IF @TranCounter = 0 
       COMMIT TRANSACTION; 
     END TRY 
     BEGIN CATCH 
      IF @TranCounter = 0 
       ROLLBACK TRANSACTION; 
      ELSE 
       IF XACT_STATE() <> -1 
        ROLLBACK TRANSACTION ProcedureSave; 

      DECLARE @ErrorMessage NVARCHAR(4000); 
      DECLARE @ErrorSeverity INT; 
      DECLARE @ErrorState INT; 
      SELECT @ErrorMessage = ERROR_MESSAGE(); 
      SELECT @ErrorSeverity = ERROR_SEVERITY(); 
      SELECT @ErrorState = ERROR_STATE(); 

      RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState); 
     END CATCH 
    GO 
相關問題