XACT_ABORT AÇIK olarak ayarlandığında hangi durumlarda CATCH bloğunun içinden bir işlem yapılabilir?


13

MSDN hakkında TRY...CATCHve hakkında okuyorum XACT_STATE.

Bu kullanımlar takip eden örnek vardır XACT_STATEiçinde CATCHbir blok TRY…CATCHyapısı işlemek veya bir işlem geri olup olmadığını belirlemek için:

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

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. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Test XACT_STATE for 0, 1, or -1.
    -- If 1, the transaction is committable.
    -- If -1, the transaction is uncommittable and should 
    --     be rolled back.
    -- 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 '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 'The transaction is committable.' + 
              ' Committing transaction.'
        COMMIT TRANSACTION;   
    END;
END CATCH;
GO

Anlamadığım şey, neden XACT_STATEgeri dönüşleri umursamalı ve kontrol etmeliyim ?

Bayrağın örnekte XACT_ABORTolarak ayarlandığını lütfen unutmayın ON.

TRYBlok içinde yeterince ciddi bir hata varsa , kontrol devreye girecektir CATCH. Yani, CATCHiçerideysem, işlemin bir sorunu olduğunu biliyorum ve bu durumda yapılacak tek mantıklı şey, onu geri almaktır, değil mi?

Ancak, MSDN'den gelen bu örnek, denetimin geçirildiği durumlar olabileceğini CATCHve yine de işlemin yapılması mantıklı olduğunu ima eder . Birisi ne zaman olabileceği, mantıklı olduğu zaman pratik bir örnek verebilir mi?

Hangi durumlarda kontrolün ayarlandığı zamanCATCH yapılabilecek bir işlemle içeri aktarılabileceğini göremiyorum .XACT_ABORTON

SET XACT_ABORTBir işlem içindeki bazı ifadeler başarılı bir şekilde yürütüldüğünde ve bazıları XACT_ABORTolarak ayarlandığında başarısız olduğunda , MSDN makalesinde bir örnek var OFF, anlıyorum. Ancak, blok içinde 1 döndüren SET XACT_ABORT ONnasıl olabilir ?XACT_STATE()CATCH

Başlangıçta, bu kodu şöyle yazmış olurdum:

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

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. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    PRINT 'Rolling back transaction.';
    ROLLBACK TRANSACTION;
END CATCH;
GO

Max Vernon'un cevabını dikkate alarak, kodu böyle yazardım. Denemeden önce aktif bir işlem olup olmadığını kontrol etmenin mantıklı olduğunu gösterdi ROLLBACK. Yine de, birlikte blokta ya hiç işlem veya herhangi bir işlemi mahkum olması olabilir. Yani, her durumda yapacak bir şey yok . Yanlış mıyım?SET XACT_ABORT ONCATCHCOMMIT

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

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. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    IF (XACT_STATE()) <> 0
    BEGIN
        -- There is still an active transaction that should be rolled back
        PRINT 'Rolling back transaction.';
        ROLLBACK TRANSACTION;
    END;

END CATCH;
GO

Yanıtlar:


8

Bu işlem olabilir çıkıyor değil içeriden taahhüt edilmesi CATCHhalinde bloğa XACT_ABORTolarak ayarlanır ON.

MSDN'den gelen örnek biraz yanıltıcıdır, çünkü kontrol XACT_STATEbazı durumlarda 1 dönebilir COMMITve işlem için mümkün olabilir .

IF (XACT_STATE()) = 1
BEGIN
    PRINT 'The transaction is committable.' + 
          ' Committing transaction.'
    COMMIT TRANSACTION;   
END;

Bu doğru değil, eğer ayarlanırsa XACT_STATEasla 1 iç CATCHblok döndürmez .XACT_ABORTON

MSDN örnek kodunun, ayara XACT_STATE()bakılmaksızın öncelikle işlevin kullanımını göstermesi amaçlanmıştır XACT_ABORT. Örnek kod XACT_ABORT, ONve olarak ayarlanmış şekilde çalışmak için yeterince genel görünüyor OFF. Sadece XACT_ABORT = ONçek ile IF (XACT_STATE()) = 1gereksiz hale gelir.


Erland Sommarskog tarafından SQL Server'da Hata ve İşlem İşleme ile ilgili çok iyi detaylı makaleler bulunmaktadır. In Bölüm 2 - Hatalar sınıflandırılması o kapsamlı bir tablo sunduğu bir araya koyar hataların bütün sınıf ve onlar SQL Server tarafından işlenir ve nasıl nasıl TRY ... CATCHve XACT_ABORTdavranışını değiştirir.

+-----------------------------+---------------------------++------------------------------+
|                             |     Without TRY-CATCH     ||        With TRY-CATCH        |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
|              SET XACT_ABORT |  OFF  |  ON   | OFF | ON  ||    ON or OFF     | OFF | ON  |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Class Name                  |    Aborts     |   Rolls   ||    Catchable     |   Dooms   |
|                             |               |   Back    ||                  |transaction|
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Fatal errors                |  Connection   |    Yes    ||       No         |    n/a    |
| Batch-aborting              |     Batch     |    Yes    ||       Yes        |    Yes    |
| Batch-only aborting         |     Batch     | No  | Yes ||       Yes        | No  | Yes |
| Statement-terminating       | Stmnt | Batch | No  | Yes ||       Yes        | No  | Yes |
| Terminates nothing at all   |    Nothing    |    No     ||       Yes        | No  | Yes |
| Compilation: syntax errors  |  (Statement)  |    No     ||       Yes        | No  | Yes |
| Compilation: binding errors | Scope | Batch | No  | Yes || Outer scope only | No  | Yes |
| Compilation: optimisation   |     Batch     |    Yes    || Outer scope only |    Yes    |
| Attention signal            |     Batch     | No  | Yes ||       No         |    n/a    |
| Informational/warning msgs  |    Nothing    |    No     ||       No         |    n/a    |
| Uncatchable errors          |    Varying    |  Varying  ||       No         |    n/a    |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+

Tablodaki son sütun soruyu cevaplar. İşlem TRY-CATCHile birlikte ve XACT_ABORT ONmümkün olan tüm durumlarda mahkum edilir.

Sorunun kapsamı dışında bir not. Erland söylediği gibi bu tutarlılık setine nedenlerinden biridir XACT_ABORTiçin ON:

Saklı yordamlarınızın komutu içermesi tavsiyesini verdim SET XACT_ABORT, NOCOUNT ON. Yukarıdaki tabloya bakarsanız, XACT_ABORTaslında daha yüksek bir tutarlılık seviyesi olduğunu görürsünüz . Örneğin, işlem her zaman mahkumdur. Aşağıda, bu varsayılan ayardan neden kaçınmanız gerektiğini anlayabilmeniz XACT_ABORTiçin nereye ayarladığım birçok örnek göstereceğim OFF.


7

Buna farklı yaklaşırdım. XACT_ABORT_ONbir kızak çekiçidir, daha rafine bir yaklaşım kullanabilirsiniz, bkz. İstisna işleme ve iç içe işlemler :

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
go

Bu yaklaşım, mümkünse, sadece TL bloğunda yapılan işi geri alacak ve TL bloğuna girmeden önce devlete geri dönecektir. Bu şekilde, bir hata durumunda tüm işi kaybetmeden, imleci yineleme gibi karmaşık işlemler yapabilirsiniz. Tek dezavantajı, işlem kayıt noktalarını kullanarak, dağıtılmış işlemler gibi kayıt noktalarıyla uyumlu olmayan herhangi bir şeyi kullanmanızın kısıtlanmasıdır.


Yanıtınız için teşekkür ederim ama soru ayarlaması gerekip gerekmediğini gerçekten değil XACT_ABORThiç ONya OFF.
Vladimir Baranov

7

TL; DR / Yönetici Özeti: Sorunun bu kısmı ile ilgili olarak:

Hangi durumlarda kontrolün ayarlandığı zamanCATCH yapılabilecek bir işlemle içeri aktarılabileceğini göremiyorum .XACT_ABORTON

Şimdi bu konuda test biraz yapmış ve herhangi davaları bulamıyorum XACT_STATE()döner 1içinde bir CATCHbloğun @@TRANCOUNT > 0 ve oturumu özelliği XACT_ABORTolduğunu ON. Ve aslında, SET XACT_ABORT için geçerli MSDN sayfasına göre :

SET XACT_ABORT AÇIK olduğunda, bir Transact-SQL deyimi bir çalışma zamanı hatası ortaya çıkarsa, tüm işlem sonlandırılır ve geri alınır.

Bu ifadenin spekülasyonlarınız ve bulgularım ile uyumlu olduğu anlaşılıyor.

MSDN makale yaklaşık SET XACT_ABORTbir hareket içinde bazı deyimleri başarıyla yürütür bir örnek vardır ve bazı başarısız XACT_ABORTolarak ayarlanırOFF

Doğru, ancak bu örnekteki ifadeler bir blok içinde değilTRY . Bir dahilinde olanlar aynı ifadeleri TRYblokta hala hataya neden birinden sonra herhangi açıklamalardan yürütülmesini engelleyecek, ama bu varsayarak XACT_ABORTolduğunu OFFkontrol geçirildiğinde, CATCHİşlem hala önceki tüm değişiklikleri hatasız oldu ki fiziksel olarak geçerlidir blokta ve edebilirsiniz arzularınınaynı ise, taahhüt olması, ya da geri alınıyor olabilir. Öte yandan, XACT_ABORTbir ONsonra herhangi bir önceki değişiklikleri otomatik geri alınır ve daha sonra ya seçeneği verilmiştir: a) sorunuROLLBACKİşlemin zaten geri alındığı ve eksi sıfırlandığı @@TRANCOUNTiçin geri alındığı 0veya b) bir hata aldığı için çoğunlukla durumun kabulüdür . Seçim yok, değil mi?

Bu bulmacanın belgelerinde belirgin olmayan muhtemelen önemli bir ayrıntı SET XACT_ABORT, bu oturum özelliğinin ve bu örnek kodun bile SQL Server 2000'den beri (belgelerin sürümler arasında neredeyse aynı TRY...CATCHolduğu), yine o belgelere baktığımızda ve (örneğin bakarak SQL Server 2005'te tanıtılan olmadanTRY...CATCH kullanarak,) XACT_ABORT ONneden olan bir acil İşlemin rulo geri alınması: hiç söz de olmadığını "işlenemez" (lütfen not hiçbir İşlem devlet yoktur bu SET XACT_ABORTdokümanda "taahhüt edilemez" bir İşlem durumu ).

Bence şu sonuca varmak makul:

  1. TRY...CATCHSQL Server 2005'te yapının tanıtımı yeni bir İşlem durumuna (yani "aktarılamaz") ve XACT_STATE()bu bilgileri alma işlevine ihtiyaç duydu .
  2. XACT_STATE()bir CATCHblokta kontrol etmek sadece aşağıdakilerin her ikisi de doğruysa mantıklıdır:
    1. XACT_ABORTolduğu OFF(başka XACT_STATE()zaman dönmelidir -1ve @@TRANCOUNTtüm ihtiyacınız olacaktır)
    2. Sen mantığı var CATCHdeğiştikçe (yapar aramalar iç içe eğer zinciri, yukarı bloğun veya bir yere COMMITve hatta herhangi bir DML, DDL, vb tablosu) yerine bir yapıyor ROLLBACK. (bu çok atipik bir kullanım örneğidir) ** lütfen Microsoft'un her zaman XACT_STATE()yerine kontrol etmesi için resmi olmayan bir öneri @@TRANCOUNTve testlerin neden gerekçelerinin ortadan kalkmadığını gösterdiğini gösteren GÜNCELLEŞTİRME 3 bölümünün altındaki nota bakın .
  3. TRY...CATCHSQL Server 2005'te yapının tanıtımı , çoğunlukla XACT_ABORT ON, İşlem üzerinde daha büyük bir denetim düzeyi sağladığı için oturum özelliğini geçersiz kılmıştır (en azından geri dönmemesi COMMITkoşuluyla, en azından seçeneğiniz vardır ). Buna bakmanın bir başka yolu, SQL Server 2005'ten önce , bir hata oluştuğunda her bir ifadeden sonraki kontrollere kıyasla işlemeyi durdurmak için kolay ve güvenilir bir yol sağlamaktır .XACT_STATE()-1
    XACT_ABORT ON@@ERROR
  4. İçin dokümantasyon örnek kodu XACT_STATE()hatalı veya en iyi yanıltıcıdır, çünkü XACT_STATE() = 1ne zaman XACT_ABORTolduğunu kontrol eder ON.

Uzun kısmı ;-)

Evet, MSDN'deki bu örnek kod biraz kafa karıştırıcı (ayrıca bkz. @@ TRANCOUNT (Geri Alma) ve XACT_STATE ) ;-). Ve bunun (Sorduğunuz bu nedenle: hatta bir "yürütülebilirken" işlem olabilir hiç mantıklı bunun nedeni ya gösterileri şey yanıltıcıdır hissediyorum CATCHblokta XACT_ABORTise ON), hatta mümkünse onu hala çok azının isteyeceği veya ihtiyaç duyacağı teknik bir olasılık üzerinde yoğunlaşıyor ve kişinin buna ihtiyaç duyma olasılığını görmezden geliyor.

TRY bloğu içinde yeterince ciddi bir hata varsa, kontrol CATCH'a geçer. Yani, eğer CATCH'in içindeysem, işlemin bir sorunu olduğunu ve bu durumda yapılacak tek mantıklı şeyin onu geri almak olduğunu biliyorum, değil mi?

Bazı kelimeler ve kavramlarla ne kastedildiğine ilişkin olarak aynı sayfada olduğumuzdan emin olsaydık yardımcı olacağını düşünüyorum:

  • "yeterince ciddi hata": Açık olmak gerekirse , TRY ... CATCH çoğu hatayı yakalayacaktır. Yakalanmayacak olanların listesi, o bağlantılı MSDN sayfasında, "TRY… CATCH Construct tarafından etkilenmeyen hatalar" bölümünde listelenmiştir.

  • "CATCH'ın içindeyim, işlemin bir sorunu olduğunu biliyorum " (em phas eklenir): "İşlem" ile ifadeleri açık bir işleme gruplayarak belirlediğiniz mantıksal iş birimini kastediyorsanız, Büyük olasılıkla evet. Çoğumuz DB millet, geri dönüşün "yapılacak tek mantıklı şey" olduğu konusunda hemfikir oluruz, çünkü açık işlemleri nasıl ve neden kullandığımızı ve hangi adımların atomik bir birimi oluşturması gerektiğini düşündüğümüze benzer bir bakış açısına sahibiz. işin.

    Ancak, açık işleme gruplandırılan gerçek iş birimlerini kastediyorsanız, hayır, işlemin kendisinde bir sorun olduğunu bilmiyorsunuzdur. Yalnızca açıkça tanımlanmış işlemde yürütülen bir ifadenin bir hata oluşturduğunu bilirsiniz . Ancak bir DML veya DDL ifadesi olmayabilir. Ve bir DML ifadesi olsa bile, İşlemin kendisi hala kararlı olabilir.

Yukarıda değinilen iki nokta göz önüne alındığında, büyük olasılıkla "yapamayacağınız" işlemler ile yapmasını istemediğiniz işlemler arasında bir ayrım yapmalıyız.

A XACT_STATE()döndürdüğünde 1, İşlemin "yüklenebilir" olduğu, COMMITveya arasında bir seçeneğiniz olduğu anlamına gelir ROLLBACK. Sen olmayabilir istiyorum bunu işlemek için, ancak bazı zor hatta gelene kadar olan-bir-örnek-sebebi sen istediğini için eğer en azından sen İşlemin bazı bölümleri başarıyla tamamlamak yaptım çünkü.

Ancak XACT_STATE()a döndüğünde -1, işlemin bir ROLLBACKkısmı kötü duruma geldiğinden gerçekten yapmanız gerekir . Şimdi, eğer kontrol CATCH bloğuna geçildiyse, o zaman kontrol etmek yeterli olur @@TRANCOUNT, çünkü İşlemi gerçekleştirebilseniz bile, neden yapmak istersiniz?

Ancak, örneğin üst kısmında fark ederseniz, XACT_ABORT ONdeğişikliklerin yapılması biraz değişiklik gösterir. Yaptıktan sonra, düzenli bir hata olabilir BEGIN TRANzaman o CATCH blok kontrolü geçecek XACT_ABORTolan OFFve Xact_State () döndürecektir 1. ANCAK, XACT_ABORT ise ON, herhangi bir hata için İşlem "durdurulur" (geçersiz kılınır) ve sonra XACT_STATE()geri döner -1. Bu durumda, kontrol etmek işe yaramaz görünüyor XACT_STATE()içinde CATCHher zaman bir dönüş göründüğü gibi bloğun -1zaman XACT_ABORTolduğu ON.

O zaman ne XACT_STATE()için? Bazı ipuçları:

  • TRY...CATCH"Taranamayan İşlemler ve XACT_STATE" bölümünün altındaki MSDN sayfası şöyle diyor:

    Bir TL bloğunun dışındaki bir işlemi normal olarak sonlandıran bir hata, bir TL bloğunun içinde hata oluştuğunda bir işlemin ayrılmaz bir duruma girmesine neden olur.

  • "Açıklamalar" bölümü altındaki SET XACT_ABORT için MSDN sayfası şöyle diyor:

    SET XACT_ABORT KAPALI olduğunda, bazı durumlarda yalnızca hatayı yükselten Transact-SQL deyimi geri alınır ve işlem işlemeye devam eder.

    ve:

    XACT_ABORT, SQL Server da dahil olmak üzere çoğu OLE DB sağlayıcısına karşı örtülü veya açık bir işlemde veri değiştirme ifadeleri için AÇIK olarak ayarlanmalıdır.

  • "Açıklamalar" bölümü altındaki BEGIN TRANSACTION için MSDN sayfası şöyle diyor:

    BEGIN TRANSACTION deyimi tarafından başlatılan yerel işlem, deyim gerçekleştirilmeden veya geri alınmadan önce aşağıdaki eylemler gerçekleştirilirse dağıtılmış bir işleme yükseltilir:

    • Bağlı bir sunucudaki uzak tabloya başvuran bir INSERT, DELETE veya UPDATE deyimi yürütülür. Bağlı sunucuya erişmek için kullanılan OLE DB sağlayıcısı ITransactionJoin arabirimini desteklemiyorsa INSERT, UPDATE veya DELETE deyimi başarısız olur.

En uygun kullanım, Bağlı Sunucu DML deyimleri bağlamında görünmektedir. Ve sanırım yıllar önce buna kendim girdim. Tüm detayları hatırlamıyorum, ancak uzak sunucunun mevcut olmamasıyla bir ilgisi vardı ve bir nedenden dolayı, bu hata TL bloğunda yakalanmadı ve CATCH'a hiç gönderilmedi ve bu yüzden yaptı olmamalı bir KOMİT. Tabii ki olabilir olmamasından meselesi olmuştur XACT_ABORTseti için ONyerine kontrol etmek başarısız XACT_STATE(), ya da muhtemelen her ikisi. Bağlantılı Sunucular ve / veya Dağıtılmış İşlemler kullanıyorsanız XACT_ABORT ONve / veya kullanmanız gerektiğinde söylenen bir şeyi okuduğumu hatırlıyorum XACT_STATE(), ancak bu belgeyi şimdi bulamıyorum. Eğer bulursam, bunu linkle güncelleyeceğim.

Yine de, birkaç şey denedim ve raporlama ile bloğa XACT_ABORT ONkontrol eden ve geçen bir senaryo bulamıyorum .CATCHXACT_STATE()1

Öğesinin XACT_ABORTdeğeri üzerindeki etkisini görmek için bu örnekleri deneyin XACT_STATE():

SET XACT_ABORT OFF;

BEGIN TRY
    BEGIN TRAN;

    SELECT 1/0 AS [DivideByZero]; -- error, yo!

    COMMIT TRAN;
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]

    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK;
    END;
END CATCH;

GO ------------------------------------------------

SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRAN;

    SELECT 1/0 AS [DivideByZero]; -- error, yo!

    COMMIT TRAN;
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]

    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK;
    END;
END CATCH;

GO ------------------------------------------------

SET XACT_ABORT ON;

BEGIN TRY
    SELECT 1/0 AS [DivideByZero]; -- error, yo!
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]
END CATCH;

GÜNCELLEME

Orijinal Sorunun bir parçası olmasa da, bu Yanıttaki bu yorumlara dayanarak:

Ben Erland'ın makalelere okuyordum hata işleme ve İşlem o yazan yere XACT_ABORTise OFFeski nedenleriyle varsayılan olarak ve normalde biz bunu belirlesin ON.
...
"... öneriyi uygular ve SET XACT_ABORT ON ile çalıştırırsanız, işlem her zaman mahkum olur."

XACT_ABORT ONHer yerde kullanmadan önce şu soruyu soracağım: burada tam olarak ne kazanılıyor? Sadece gerekli olduğunda kullanmanız gerektiğini ve genellikle savunmayı gerekli bulamadım. ROLLBACK@ Remus'un cevabında gösterilen şablonu veya bu cevapta gösterildiği gibi esasen aynı şey ama Kaydetme Noktası olmadan yıllardır kullandığım şablonu kullanarak kolayca idare edip edemeyeceğiniz (bu cevapta gösterildiği gibi) ( iç içe çağrıları yönetir):

İşlemi C # Kodunda ve saklı yordamda işlememiz gerekiyor mu


GÜNCELLEME 2

Biraz daha test yaptım, bu sefer küçük bir .NET Konsol Uygulaması oluşturarak, uygulama katmanında bir İşlem oluşturarak, herhangi bir SqlCommandnesneyi (yani yoluyla using (SqlTransaction _Tran = _Connection.BeginTransaction()) { ...) çalıştırmadan önce ve sadece bir ifade yerine toplu iptal hatası kullanarak - iptal hatası ve şunu buldu:

  1. "Değişken" bir İşlem, çoğunlukla geri alınmış (değişiklikler geri alınmıştır), ancak @@TRANCOUNTyine de> 0 olan bir İşlemdir .
  2. "Değişken" bir İşleminiz COMMITolduğunda, İşlemin "bölünemez" olduğunu söyleyen bir hata oluşturacak şekilde a düzenleyemezsiniz. Ayrıca, toplu işlem tamamlanamayan, işlenemez bir işlemle tamamlandığını ve geri alınacağını belirten bir hata oluşturulacağı için bunu göz ardı edemez / hiçbir şey yapamazsınız (yani, yine de otomatik olarak geri dönecekse, neden hata atmaya zahmet?). Eğer Yani gerekir açık bir sorunu ROLLBACKderhal belki değil, CATCHbloğun ama toplu sona ermeden.
  3. Bir de TRY...CATCHne zaman yapı, XACT_ABORTolduğu OFF, İşlem sonlandırmak istiyorum hatalar otomatik olarak bir dış oluştu vardı TRY"uncommitable" olarak bırakarak Tranasction sonlandırmak iş geri ama olmaz, böyle toplu iptal hatalar olarak, bloğun. A ROLLBACKvermek, İşlemi kapatmak için gereken bir formaliteden ibarettir, ancak çalışma zaten geri alınmıştır.
  4. Ne zaman XACT_ABORTbir ONçoğu hata toplu-iptal olarak hareket eden ve doğrudan doğruya yukarıda (# 3) mermi noktası tarif edildiği gibi bu nedenle davranır.
  5. XACT_STATE(), en azından bir CATCHblokta, -1hata sırasında etkin bir İşlem varsa toplu iptal hataları için bir gösterecektir .
  6. XACT_STATE()bazen 1etkin bir İşlem olmasa bile geri döner . Eğer @@SPID(diğerleri arasında) olduğu SELECTbirlikte listede XACT_STATE(), daha sonra XACT_STATE()etkin İşlem olduğunda 1 dönecektir. Bu davranış, SQL Server 2012'de başladı ve 2014'te var, ancak 2016'da test etmedim.

Yukarıdaki hususlar göz önünde bulundurularak:

  • Verilen noktaları hataları "uncommitable" Bir İşlem hale getirecek en (veya tüm?) Beri 4. ve 5., bu kontrol etmek tamamen anlamsız görünüyor XACT_STATE()içinde CATCHzaman bloğunda XACT_ABORTolduğu ONzaman olacaktır döndürülen değerin beri -1.
  • Denetleme XACT_STATE()içinde CATCHzaman blokta XACT_ABORTise OFFonu dönecektir beri dönüş değeri en azından bazı varyasyon olacağı için daha mantıklı 1açıklama-iptal hataları. Ancak, çoğumuz gibi kod yazarsanız, o zaman bu ayrım anlamsızdır, çünkü ROLLBACKyine de bir hata oluştuğu için zaten çağıracaksınız .
  • Eğer emri bir verme yapan bir durum bulursanız COMMITiçinde CATCHbloğun, daha sonra değerini kontrol XACT_STATE()ve emin olun SET XACT_ABORT OFF;.
  • XACT_ABORT ONTRY...CATCHyapı üzerinde çok az fayda var ya da hiç fayda görmüyor.
  • Kontrol XACT_STATE()etmenin sadece kontrol etmekten anlamlı bir fayda sağladığı hiçbir senaryo bulamıyorum @@TRANCOUNT.
  • Nerede ayrıca hiçbir senaryo bulabilirsiniz XACT_STATE()döner 1bir de CATCHbloğu zaman XACT_ABORTolduğu ON. Bunun bir dokümantasyon hatası olduğunu düşünüyorum.
  • Evet, açıkça başlamadığınız bir İşlemi geri alabilirsiniz. Ve kullanma bağlamında, XACT_ABORT ONbir tartışma noktasıdır, çünkü bir TRYblokta meydana gelen bir hata değişiklikleri otomatik olarak geri alır.
  • TRY...CATCHYapı üzerine yararı vardır XACT_ABORT ONotomatik olarak değil bütün işlem iptal ve dolayısıyla (sürece İşlem izin vererek XACT_STATE()geri dönüş 1(bu bir kenar durumda bile) kararlı olmak).

Örnek XACT_STATE()geri -1zaman XACT_ABORTolduğu OFF:

SET XACT_ABORT OFF;

BEGIN TRY
    BEGIN TRAN;

    SELECT CONVERT(INT, 'g') AS [ConversionError];

    COMMIT TRAN;
END TRY
BEGIN CATCH
    DECLARE @State INT;
    SET @State = XACT_STATE();
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            @State AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage];

    IF (@@TRANCOUNT > 0)
    BEGIN
        SELECT 'Rollin back...' AS [Transaction];
        ROLLBACK;
    END;
END CATCH;

GÜNCELLEME 3

GÜNCELLEŞTİRME 2 bölümündeki 6. maddeyle ilgili ( XACT_STATE()etkin bir İşlem olmadığında döndürülen olası yanlış değer ):

  • Tek / hatalı davranış SQL Server 2012'de başladı (şimdiye kadar 2012 SP2 ve 2014 SP1'e göre test edildi)
  • SQL Server sürüm 2005, 2008 ve 2008 R2'de, XACT_STATE()Tetikleyiciler veya INSERT...EXECsenaryolarda kullanıldığında beklenen değerler bildirilmedi : xact_state (), bir işlemin mahkum olup olmadığını belirlemek için güvenilir bir şekilde kullanılamaz . Ancak, bu 3 sürümleri (sadece 2008 R2 üzerinde test) 'de, XACT_STATE()yok değil yanlış rapor 1bir kullanıldığında SELECTile @@SPID.
  • Burada belirtilen davranışa karşı bir Connect hatası var ancak "Tasarım gereği " olarak kapatıldı: XACT_STATE () SQL 2012'de yanlış bir işlem durumu döndürebilir . Bununla birlikte, test bir DMV arasından seçim yapılırken yapıldı ve bunun en azından bazı DMV'ler için doğal olarak sistem tarafından oluşturulmuş bir işleme sahip olacağı sonucuna varıldı. MS tarafından verilen son yanıtta ayrıca:

    Bir IF deyiminin ve ayrıca FROM içermeyen bir SELECT seçeneğinin bir işlem başlatmadığını unutmayın.
    örneğin, daha önce var olan bir işleminiz yoksa SELECT XACT_STATE () çalıştırıldığında 0 döndürülür.

    Aşağıdaki ifadeler göz önüne alındığında bu ifadeler yanlıştır:

    SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @@SPID AS [SPID];
    GO
    DECLARE @SPID INT;
    SET @SPID = @@SPID;
    SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @SPID AS [SPID];
    GO
    
  • Bu nedenle, yeni Connect hatası:
    XACT_STATE (), SELECT içinde bazı sistem değişkenleriyle ancak FROM yan tümcesi olmadan kullanıldığında 1 değerini döndürür

LÜTFEN "XACT_STATE () içinde SQL 2012'de yanlış bir işlem durumu döndürebilir" Bağlantı öğesi doğrudan yukarıda bağlanmıştır, Microsoft (iyi, bir temsilcisi) şunu belirtir:

@@ trancount, BEGIN TRAN ifadelerinin sayısını döndürür. Dolayısıyla, aktif bir işlem olup olmadığının güvenilir bir göstergesi değildir. XACT_STATE () ayrıca etkin bir otomatik taahhüt işlemi varsa 1 değerini döndürür ve bu nedenle etkin bir işlem olup olmadığının daha güvenilir bir göstergesidir.

Ancak güvenmemek için hiçbir neden bulamıyorum @@TRANCOUNT. Aşağıdaki test, otomatik taahhüt işleminde @@TRANCOUNTgerçekten geri döndüğünü gösterir 1:

--- begin setup
GO
CREATE PROCEDURE #TransactionInfo AS
SET NOCOUNT ON;
SELECT @@TRANCOUNT AS [TranCount],
       XACT_STATE() AS [XactState];
GO
--- end setup

DECLARE @Test TABLE (TranCount INT, XactState INT);

SELECT * FROM @Test; -- no rows

EXEC #TransactionInfo; -- 0 for both fields

INSERT INTO @Test (TranCount, XactState)
    EXEC #TransactionInfo;

SELECT * FROM @Test; -- 1 row; 1 for both fields

Ayrıca bir Tetikleyici ile gerçek bir tablo üzerinde de test ettim ve Tetik @@TRANCOUNTdahilinde, 1net bir İşlem başlamamasına rağmen doğru bir şekilde rapor verdim .


4

Defansif programlama, mümkün olduğu kadar çok bilinen durumu işleyen kod yazmanızı ve böylece hata olasılığını azaltmanızı gerektirir.

Geri alma işleminin yürütülüp yürütülemeyeceğini belirlemek için XACT_STATE () öğesini kontrol etmek iyi bir uygulamadır. Körü körüne bir geri alma girişimi, yanlışlıkla ... ... CATCH'ınızın içinde bir hataya neden olabileceğiniz anlamına gelir.

TL geri alımında bir geri alma başarısız olabilir ... CATCH, açıkça bir işlem başlatmadıysanız olurdu. Kod bloklarını kopyalayıp yapıştırmak kolayca buna neden olabilir.


Cevabınız için teşekkür ederim. Basit ROLLBACKolanın içinde işe yaramayacağı bir durum düşünemedim CATCHve iyi bir örnek verdiniz. Sanırım, iç içe geçmiş işlemlerin ve iç içe geçmiş saklı yordamların kendi TRY ... CATCH ... ROLLBACKiçinde yer alması hızlı bir şekilde dağınık hale gelebilir .
Vladimir Baranov

Yine de, ikinci bölümle ilgili cevabınızı uzatabilirseniz sevinirim - Taahhütlü işlemle bloğun IF (XACT_STATE()) = 1 COMMIT TRANSACTION; içine nasıl girebiliriz CATCH? İçinden (olası) bazı çöpleri atmaya cesaret edemem CATCH. Benim akıl yürütme: eğer bir CATCHşeylerin içinde yanlış gidersek, veritabanının durumuna güvenemem, bu yüzden ROLLBACKelimizde ne varsa daha iyi olur .
Vladimir Baranov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.