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:
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 .
XACT_STATE()bir CATCHblokta kontrol etmek sadece aşağıdakilerin her ikisi de doğruysa mantıklıdır:
XACT_ABORTolduğu OFF(başka XACT_STATE()zaman dönmelidir -1ve @@TRANCOUNTtüm ihtiyacınız olacaktır)
- 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 .
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
- İç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:
- "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 .
- "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.
- 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.
- 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.
XACT_STATE(), en azından bir CATCHblokta, -1hata sırasında etkin bir İşlem varsa toplu iptal hataları için bir gösterecektir .
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 .
XACT_ABORThiçONyaOFF.