जब Xact_abort चालू होता है तो Sql Server किशमिश के बाद क्यों काम करता है?


87

मैं अभी TSQL में कुछ से हैरान हो गया। मुझे लगा कि अगर xact_abort चालू होता, तो कुछ ऐसा कहते

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

संग्रहीत कार्यविधि (या किसी बैच) के निष्पादन को रोक देगा।

लेकिन मेरा ADO.NET त्रुटि संदेश ठीक इसके विपरीत साबित हुआ। मुझे अपवाद संदेश में राइसरर त्रुटि संदेश, और इसके बाद टूटने वाली अगली बात दोनों मिली।

यह मेरा वर्कअराउंड है (जो वैसे भी मेरी आदत है), लेकिन ऐसा नहीं लगता कि यह आवश्यक होना चाहिए:

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

डॉक्स यह कहते हैं:

जब SET XACT_ABORT चालू होता है, यदि कोई Transact-SQL कथन रन-टाइम त्रुटि उठाता है, तो संपूर्ण लेन-देन समाप्त हो जाता है और वापस चला जाता है।

क्या इसका मतलब है कि मुझे स्पष्ट लेनदेन का उपयोग करना चाहिए?


RAISERRORअगर वास्तव में गंभीरता को 16 के बजाय 17 या 18 पर सेट किया गया है, तो बस परीक्षण और वसीयत को समाप्त कर देगा।
सुधार हुआ '

2
सिर्फ SQL सर्वर 2012 का परीक्षण किया और RAISERRORवास्तव में निष्पादन को समाप्त नहीं करेगा यदि गंभीरता 16 के बजाय 17 या 18 पर सेट हो।
इयान बॉयड

1
इसका कारण स्पष्ट रूप से Erland Sommarskog (SQL Server MVP 2001) के बाद से SQL Server में उनकी उत्कृष्ट श्रृंखला त्रुटि और लेन-देन हैंडलिंग के भाग 2 में स्पष्ट रूप से बताया गया है : "हर एक बार थोड़ी देर में, मुझे यह महसूस होता है कि SQL सर्वर जानबूझकर बनाया गया है संभव के रूप में भ्रमित करना। जब वे एक नई रिलीज़ की योजना बनाते हैं, तो वे एक-दूसरे से पूछते हैं कि हम उपयोगकर्ताओं को भ्रमित करने के लिए इस समय क्या कर सकते हैं? कभी-कभी वे विचारों से थोड़ा बाहर निकलते हैं, लेकिन फिर कोई कहता है कि चलो त्रुटि से निपटने के साथ कुछ करें! "
उल्टा इंजीनियर

@IanBoyd, वास्तव में, 17 या 18 या 19 को गंभीरता निर्धारित करने के बाद भी निष्पादन बंद नहीं हुआ है । क्या अधिक दिलचस्प है, यदि आप Messagesटैब में देखते हैं तो आपको कोई संदेश (X rows affected)या PRINTसंदेश दिखाई नहीं देंगे , जो मैं कहूंगा कि एक पूर्ण झूठ है !
गेब्रियलियस

जवाबों:


48

यह डिज़ाइन TM द्वारा है , जैसा कि आप SQL सर्वर टीम द्वारा कनेक्ट पर एक समान प्रश्न के जवाब में देख सकते हैं :

आपकी प्रतिक्रिया के लिए धन्यवाद। डिज़ाइन द्वारा, XACT_ABORT सेट विकल्प RAISERROR कथन के व्यवहार को प्रभावित नहीं करता है। SQL सर्वर की भावी रिलीज़ के लिए इस व्यवहार को संशोधित करने के लिए हम आपकी प्रतिक्रिया पर विचार करेंगे।

हां, यह उन लोगों के लिए एक मुद्दा है जो RAISERRORउच्च गंभीरता के साथ आशा करते हैं (जैसे 16) SQL निष्पादन त्रुटि के समान होगा - यह नहीं है।

आपका वर्कअराउंड सिर्फ उसके बारे में है जो आपको करने की आवश्यकता है, और एक स्पष्ट लेनदेन का उपयोग करने से उस व्यवहार पर कोई प्रभाव नहीं पड़ता है जिसे आप बदलना चाहते हैं।


1
धन्यवाद फिलिप। आपके द्वारा संदर्भित लिंक अनुपलब्ध प्रतीत होता है।
एरिक जेड बियर्ड

2
लिंक ठीक काम कर रहा है, अगर आपको कभी भी इसके लिए खोज करने की आवश्यकता है, तो शीर्षक "है RAISERROR काम के साथ XACT_ABORT", लेखक "जोरुंडुर", आईडी: 275308
जॉन सी सी

लिंक मृत है, जिसमें कोई संग्रह नहीं है। कैश्ड कॉपी। यह हमेशा के लिए समय की रेत में खो गया है।
इयान बोयड

यह उत्तर एक अच्छा बैकअप है - डॉक्स के लिंक के साथ जहां यह व्यवहार स्पष्ट किया जाता है।
पीसीबीदेव

25

यदि आप एक कोशिश / कैच ब्लॉक का उपयोग करते हैं, तो 11-19 की गंभीरता के साथ एक किशमिश त्रुटि संख्या को पकड़ ब्लॉक में कूदने के लिए निष्पादन का कारण होगा।

16 से ऊपर की कोई भी गंभीरता एक सिस्टम त्रुटि है। निम्न कोड प्रदर्शित करने के लिए एक कोशिश / कैच ब्लॉक सेट करें और एक संग्रहीत प्रक्रिया निष्पादित करें जो हम मानते हैं कि विफल हो जाएगी:

मान लें कि हमारे पास एक तालिका है [dbo]। [त्रुटियां] त्रुटियां रखने के लिए मान लें कि हमारे पास एक संग्रहीत कार्यविधि है [dbo]। [मान लें कि यह तब होता है जब हम इसे निष्पादित करते हैं।

-- 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

इसके RETURNतुरंत बाद उपयोग करें RAISERROR()और यह आगे की प्रक्रिया को निष्पादित नहीं करेगा।


8
आप कॉल करने rollback transactionसे पहले कॉल करना चाह सकते हैं return
माइक क्रिश्चियन

1
संभवतः आपको अपने कैच ब्लॉक में कुछ करने की आवश्यकता हो सकती है
sqluser

14

जैसा कि डॉक्स पर बताया गया है SET XACT_ABORT, के THROWबजाय कथन का उपयोग किया जाना चाहिए RAISERROR

दोनों थोड़ा अलग व्यवहार करते हैं । लेकिन जब XACT_ABORTON सेट हो जाता है, तो आपको हमेशा THROWकमांड का उपयोग करना चाहिए ।


25
यदि आपके पास 2k12 नहीं है (या जब यह बाहर आता है), तो कोई थ्रॉव विवरण नहीं होना चाहिए।
जेफ मोदेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.