Xact_abort açıkken Sql Server neden yükseltmeden sonra çalışmaya devam ediyor?


88

TSQL'deki bir şeye şaşırdım. Xact_abort açıksa,

raiserror('Something bad happened', 16, 1);

saklı yordamın (veya herhangi bir toplu işin) yürütülmesini durdurabilir.

Ancak ADO.NET hata mesajım bunun tam tersini kanıtladı. Hem istisna mesajında ​​yükselen hata mesajı hem de bundan sonra bozulan bir sonraki şey var.

Bu benim geçici çözümüm (zaten alışkanlığım bu), ancak gerekli gibi görünmüyor:

if @somethingBadHappened
    begin;
        raiserror('Something bad happened', 16, 1);
        return;
    end;

Doktorlar şunu söylüyor:

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

Bu, açık bir işlem kullanmam gerektiği anlamına mı geliyor?


Henüz test edildi ve RAISERRORciddiyet 16 yerine 17 veya 18 olarak ayarlanırsa yürütmeyi sonlandıracak.
yeniden düzenlendi

2
Sadece 2012 SQL Server test edilmiş ve RAISERRORaslında edecek değil şiddeti 17 veya 18 olarak ayarlanırsa, 16. yerine, yürütme sonlandırmak
Ian Boyd

1
Bunun nedeni, Erland Sommarskog (2001'den beri SQL Server MVP) tarafından SQL Server'daki mükemmel Hata ve İşlem İşleme serisinin 2. bölümünde açıkça açıklanmaktadır : "Ara sıra, SQL Server'ın kasıtlı olarak öyle tasarlandığı hissine kapılıyorum. Mümkün olduğu kadar kafa karıştırıcı. Yeni bir sürüm planladıklarında birbirlerine bu kez kullanıcıların kafasını karıştırmak için ne yapabiliriz diye soruyorlar ? Bazen biraz fikirsiz kalıyorlar ama sonra birisi hata işleme ile bir şeyler yapalım diyor! "
Geriye döndürülmüş Mühendisi

1
@IanBoyd, gerçekten, hatta 17 veya 18 ya da 19 yürütme şiddetini ayarlandıktan sonra bir yok durdu. Daha da ilginci, Messagessekmeye bakarsanız, hayır (X rows affected)ya da PRINTmesajlar göreceksiniz , ki bunun tamamen yalan olduğunu söyleyebilirim !
Gabrielius

Yanıtlar:


48

Connect'te SQL Server ekibinin benzer bir soruya verdiği yanıtta görebileceğiniz gibi bu, By Design TM'dir :

Geri bildiriminiz için teşekkür ederiz. Tasarım gereği, XACT_ABORT set seçeneği RAISERROR ifadesinin davranışını etkilemez. Geri bildiriminizi, SQL Server'ın gelecekteki bir sürümü için bu davranışı değiştirmek için dikkate alacağız.

Evet, bu, RAISERRORyüksek bir şiddette (gibi 16) bir SQL yürütme hatasıyla aynı olacağını ümit edenler için biraz sorun - öyle değil.

Çözümünüz, yapmanız gerekenlerle ilgilidir ve açık bir işlem kullanmanın değiştirmek istediğiniz davranış üzerinde herhangi bir etkisi yoktur.


1
Teşekkürler Philip. Başvurduğunuz bağlantı mevcut değil gibi görünüyor.
Eric Z Beard

2
Bağlantı iyi çalışıyor, eğer aramanız gerekirse, "RAISERROR'un XACT_ABORT ile çalışmasını sağlayın", yazar "jorundur", ID: 275308
JohnC

Bağlantı öldü, archive.org önbelleğe alınmış kopyası yok. Zamanın kumları arasında sonsuza kadar kayboldu.
Ian Boyd

Bu cevap iyi bir yedek - bu davranışın netleştirildiği dokümanlara bir bağlantı ile .
pcdev

25

Bir dene / yakala bloğu kullanırsanız, önem derecesi 11-19 olan bir yükseltme hatası hata numarası, yürütmenin catch bloğuna atlamasına neden olur.

16'nın üzerindeki herhangi bir önem derecesi sistem hatasıdır. Aşağıdaki kodu göstermek için bir dene / yakala bloğu kurar ve başarısız olacağını düşündüğümüz bir saklı yordamı yürütür:

bir tablomuz olduğunu varsayalım [dbo]. Hataları tutmak için [Hatalar] saklı bir yordamımız [dbo] olduğunu varsayalım. [AssumeThisFails], yürüttüğümüzde başarısız olacak

-- first lets build a temporary table to hold errors
if (object_id('tempdb..#RAISERRORS') is null)
 create table #RAISERRORS (ErrorNumber int, ErrorMessage varchar(400), ErrorSeverity int, ErrorState int, ErrorLine int, ErrorProcedure varchar(128));

-- this will determine if the transaction level of the query to programatically determine if we need to begin a new transaction or create a save point to rollback to
declare @tc as int;
set @tc = @@trancount;
if (@tc = 0)
 begin transaction;
else
 save transaction myTransaction;

-- the code in the try block will be executed
begin try
 declare @return_value = '0';
 set @return_value = '0';
 declare
  @ErrorNumber as int,
  @ErrorMessage as varchar(400),
  @ErrorSeverity as int,
  @ErrorState as int,
  @ErrorLine as int,
  @ErrorProcedure as varchar(128);


 -- assume that this procedure fails...
 exec @return_value = [dbo].[AssumeThisFails]
 if (@return_value <> 0)
  raiserror('This is my error message', 17, 1);

 -- the error severity of 17 will be considered a system error execution of this query will skip the following statements and resume at the begin catch block
 if (@tc = 0)
  commit transaction;
 return(0);
end try


-- the code in the catch block will be executed on raiserror("message", 17, 1)
begin catch
  select
   @ErrorNumber = ERROR_NUMBER(),
   @ErrorMessage = ERROR_MESSAGE(),
   @ErrorSeverity = ERROR_SEVERITY(),
   @ErrorState = ERROR_STATE(),
   @ErrorLine = ERROR_LINE(),
   @ErrorProcedure = ERROR_PROCEDURE();

  insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
   values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);

  -- if i started the transaction
  if (@tc = 0)
  begin
   if (XACT_STATE() <> 0)
   begin
     select * from #RAISERRORS;
    rollback transaction;
    insert into [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     select * from #RAISERRORS;
    insert [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
    return(1);
   end
  end
  -- if i didn't start the transaction
  if (XACT_STATE() = 1)
  begin
   rollback transaction myTransaction;
   if (object_id('tempdb..#RAISERRORS') is not null)
    insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
   else
    raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
   return(2); 
  end
  else if (XACT_STATE() = -1)
  begin
   rollback transaction;
   if (object_id('tempdb..#RAISERRORS') is not null)
    insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
     values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
   else
    raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
   return(3);
  end
 end catch
end

22

RETURNHemen sonra kullanın RAISERROR()ve prosedürü daha fazla yürütmeyecektir.


8
Aramadan rollback transactionönce aramak isteyebilirsiniz return.
Mike Christian

1
Muhtemelen yakalama bloğunuzda bir şeyler yapmanız gerekebilir
sqluser

15

Belgelerinde belirtildiği gibi SET XACT_ABORT, THROWifade yerine kullanılmalıdır RAISERROR.

İkisi biraz farklı davranıyor . Ancak XACT_ABORTON olarak ayarlandığında, her zaman THROWkomutu kullanmalısınız .


25
Eğer 2k12'ye (veya çıktığında üstü) sahip değilseniz, o zaman alınacak bir THROW ifadesi yoktur.
Jeff Moden
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.