2012-03-15 12 views
12

Kayıt noktası ayarlaması gereken bir saklı yordamım var, böylece belirli koşullar altında yaptığı her şeyi geri alabilir ve arayana bir hata kodu verebilir veya kabul et/taahhüt et ve arayana başarıyı geri ver. Ama arayanın zaten bir işlem başlatıp açmadığını çalışmak için ona ihtiyacım var. Bu konuda doktor çok kafa karıştırıcı. İşte bence işe yarayacak, ama tüm sonuçlardan emin değilim.SAVE TRANSACTION vs BEGIN TRANSACTION (SQL Server) işlemlerin nasıl güzelce yuvalanacağı

Sorun şu ki - bu Stored Procedure (SP) başkaları tarafından çağırıldı. Bu yüzden, bir işlemi başlatıp başlamadıklarını bilmiyorum ... SP'mi kullanmak için bir işlem başlatması için kullanıcılara ihtiyaç duyarsam bile, Save Points

SP, bir işlemin devam edip etmediğini sınar ve eğer değilse BEGIN TRANSACTION ile bir tane başlatır. Bir işlem devam ediyorsa, bunun yerine SAVE TRANSACTION MySavePointName ile bir kayıt noktası oluşturacak ve yaptığım şey budur.

Daha sonra değişiklikleri geri almak zorunda kalırsam, daha önce BEGIN TRANSACTION yapsaydım, ROLLBACK TRANSACTION olur. Eğer kaydetme noktasını yapsaydım, o zaman ROLLBACK TRANSACTION MySavePointName. Bu senaryo harika çalışıyor.

Burada biraz kafam karıştı - eğer yaptığım işi sürdürmek istersem, bir işlem başlatırsam COMMIT TRANSACTION'u çalıştırırım. Ama eğer bir tasarruf noktası yaratsaydım? Ben COMMIT TRANSACTION MySavePointName çalıştı, ancak daha sonra arayanın kendi hareketi tamamlamak için çalışır ve bir hata alır: Yani

The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

Sonra merak ediyorum - bir tasarruf noktası geri alınmasını (çalışır: ROLLBACK TRANSACTION MySavePointName arayanın en geri almak DEĞİL olacaktır işlem). Ama belki de hiç kimse "işlemek" zorunda değil mi? Sadece orada kalıyor, geri dönmeniz gerekiyor, ancak orijinal işlem gerçekleştiğinde (veya geri döndüğünde) kayboluyor mu?

Bir işlemi "yerleştirmek" için "daha iyi" bir yol varsa, lütfen biraz ışık da verin. BEGIN TRANSACTION ile nasıl iç içe geçeceğimi anlayamadım, ancak sadece içsel işlemlerimi geri almamı ya da taahhüt etmem. ROLLBACK, her zaman en üstteki işleme geri döner, COMMIT ise @@trancount değerini azaltır.

+0

Kişisel bulmak bir cevap olarak gönderme değebilir. –

+0

@Andriy Ok, yaptığım buydu - düzenlememi kaldırdım ve bunun yerine bir yanıt olarak kullandı. Teşekkürler. –

cevap

19

ben şimdi tüm dışarı bu anladım inanıyoruz, bu nedenle Yani http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx

benim SP adresinden daha fazla ayrıntı istiyorsanız ben bile benim bulguları blogged ettik ...

kendi sorumu cevap verecektir böyle bir şey, hiçbiri varsa, yeni bir hareketi başlatmak, ama bir zaten devam eğer Save noktasına kullanmaya başlar: hazır değişiklikleri kaydetmek için zaman

DECLARE @startingTranCount int 
SET @startingTranCount = @@TRANCOUNT 

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

Ardından, yalnızca işlemek gerekir İşlemi kendimize başlattık:

IF @startingTranCount = 0 
    COMMIT TRANSACTION 

Ve son olarak, sadece değişiklikler şimdiye kadar geri almak için:

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

, "kaydetme işleminden sonra" yeni bir iç içe işlem başlatmamanın herhangi bir nedenidir? – sotn

+0

Aşağıda yuvalama işlemleri ile ilgili bir makaleye bir bağlantıdır. Yuvalanmış bir işlem gerçekleştirilirse, iç içe geçmiş işlem sayısı azaltılır, ancak hiçbir şey YAPILMAZ. Yani eğer dış işlem gerçekleşirse, her şey, eğer geri alınırsa, "kararlı" olanı da dahil olmak üzere her şey geri alınır. Yuvalanmış işlem bir geri alma yaparsa, İKİ'nin kendisini VE dış işlem (ler) i geri alır. SADECE, içinde bulunduğunuz işlemleri etkilemeden kendi işleminizi geri almak isterseniz, burada açıklanan tekniği kullanın. –

+0

İç içe geçmiş işlemlerle ilgili bu makale şöyledir: https://technet.microsoft.com/en-us/library/ms189336%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396 –

10

Brian B's answer genişletme.

Bu, kaydetme noktası adının benzersiz olmasını sağlar ve SQL Server 2012'nin yeni TL/CATCH/THROW özelliklerini kullanır.

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

Geri dönmedim Bir süredir bu gönderiye, ancak bir sonraki sorguda sadece verbatim'i yeniden kullanabileceğiniz şablona bayılıyorum. Güzel iyileştirme. –

+0

Ben try block içinde harekete başlamak için MSDN önerileri gördüm. Bunu bu şekilde yapmak bazı sorunlara neden olur mu yoksa bunu yapmak tamamen güvenli midir? –

+0

Yakalama bloğundaki if ifadesi hakkında kafam karıştı. Neden xact state = -1 ise işlemi geri alır. Öyle değil mi <> - 1? –

2

benim procedure işlem yöneticisi bu tür kullandık:

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