จำนวนธุรกรรมหลังจาก EXECUTE ระบุจำนวนคำสั่ง BEGIN และ COMMIT ที่ไม่ตรงกัน จำนวนก่อนหน้า = 1 จำนวนปัจจุบัน = 0


95

ฉันมีInsertขั้นตอนการจัดเก็บซึ่งจะป้อนข้อมูลTable1และรับColumn1ค่าจากTable1และเรียกใช้กระบวนงานที่เก็บไว้ที่สองซึ่งจะป้อน Table2

แต่เมื่อฉันเรียกขั้นตอนการจัดเก็บที่สองเป็น:

Exec USPStoredProcName

ฉันได้รับข้อผิดพลาดต่อไปนี้:

จำนวนธุรกรรมหลังจาก EXECUTE ระบุจำนวนคำสั่ง BEGIN และ COMMIT ที่ไม่ตรงกัน จำนวนก่อนหน้า = 1 จำนวนปัจจุบัน = 0

ฉันได้อ่านคำตอบในคำถามอื่น ๆ ดังกล่าวแล้วและไม่พบว่าการนับคอมมิตเกิดความสับสนที่ใด


คุณมีบล็อก TRY / CATCH ในขั้นตอนของคุณหรือไม่?
Remus Rusanu

ใช่ฉันมีบล็อก TRY / CATCH
Vignesh Kumar A

คำตอบ:


110

หากคุณมีบล็อก TRY / CATCH สาเหตุที่เป็นไปได้คือคุณพบข้อยกเว้นในการยกเลิกธุรกรรมและดำเนินการต่อ ในบล็อก CATCH คุณต้องตรวจสอบXACT_STATE()และจัดการธุรกรรมที่ถูกยกเลิกและไม่เป็นธรรม (ถึงวาระ) ที่เหมาะสมเสมอ หากผู้โทรของคุณเริ่มทำธุรกรรมและลูกค้าได้รับผลกระทบเช่นการชะงักงัน (ซึ่งยกเลิกการทำธุรกรรม) ผู้โทรจะสื่อสารกับผู้โทรได้อย่างไรว่าธุรกรรมถูกยกเลิกและไม่ควรดำเนินธุรกิจต่อไปตามปกติ วิธีเดียวที่เป็นไปได้คือการเพิ่มข้อยกเว้นอีกครั้งโดยบังคับให้ผู้โทรจัดการกับสถานการณ์ หากคุณกลืนธุรกรรมที่ถูกยกเลิกอย่างเงียบ ๆ และผู้โทรยังคงสมมติว่ายังอยู่ในธุรกรรมเดิมมีเพียงการทำร้ายร่างกายเท่านั้นที่สามารถมั่นใจได้ (และข้อผิดพลาดที่คุณได้รับคือวิธีที่เครื่องมือพยายามป้องกันตัวเอง)

ขอแนะนำให้คุณไปที่การจัดการข้อยกเว้นและธุรกรรมที่ซ้อนกันซึ่งแสดงรูปแบบที่สามารถใช้กับธุรกรรมที่ซ้อนกันและข้อยกเว้น:

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

3
ขอบคุณสำหรับความช่วยเหลือของคุณ. โดยใช้ Raiserror ฉันพบปัญหามันเกี่ยวกับการพยายามแทรกค่า NULL ลงในฟิลด์ NOT NULL
Vignesh Kumar A

แต่การตรวจสอบความถูกต้องของการตรวจสอบข้อ จำกัด จะไม่ยกเลิกธุรกรรม คุณกำลังย้อนกลับอย่างชัดเจนในการจับหรือคุณใช้xact_abort on?
Remus Rusanu

ฉันย้อนกลับไปอย่างชัดเจน
Vignesh Kumar A

2
ฉันได้ลองใช้รูปแบบนี้แล้ว แต่ก็ยังใช้ไม่ได้ - เมื่อฉันมีธุรกรรมภายนอกรูปแบบนี้จะสร้างจุดประหยัดและในกรณีที่เกิดข้อผิดพลาดร้ายแรง (ธุรกรรมที่ไม่สามารถตกลงกันได้) จะย้อนกลับธุรกรรมภายนอกซึ่งยังคงทำให้เกิด @@ trancount = 1 ก่อนที่จะเข้าสู่ ขั้นตอนและ @@ trancount = 0 เมื่อออกจากมัน
sparrow

3
ฉันคิดว่าบิตนี้ใน CATCH ผิด: if @xstate = -1 rollback; เมื่อดูตัวอย่าง MSDNนี้เราไม่ควรย้อนกลับธุรกรรมทั้งหมดเว้นแต่จะไม่มีธุรกรรมภายนอก (นั่นคือเว้นแต่เราจะทำbegin tran) ฉันคิดว่าขั้นตอนนี้ควรใช้ก็ต่อrollbackเมื่อเราเริ่มต้นธุรกรรมซึ่งจะแก้ไขปัญหาของ @ sparrow
นิค

62

ฉันก็มีปัญหานี้เหมือนกัน สำหรับฉันเหตุผลก็คือฉันทำ

return
commit

แทน

commit
return   

ในขั้นตอนเดียวที่เก็บไว้


4
@seguso - สิ่งนี้มีประโยชน์มาก ขอบคุณสำหรับการแชร์. บางครั้งก็มีบางสิ่งอยู่ใต้ฝุ่น เกิดขึ้นกับสิ่งที่ดีที่สุดของพวกเขา
Leo Gurdian

นี่เป็นปัญหาสำหรับฉัน แต่เห็นได้ชัดน้อยกว่าเนื่องจากเราทำการตัดการโทร sproc หลายครั้งในธุรกรรมขนาดใหญ่ครั้งเดียวผ่านชั้นการเข้าถึงข้อมูลของเราดังนั้นเพียงแค่ดูที่ sproc คุณก็ไม่สามารถบอกได้ว่ามีธุรกรรมอยู่เลย หากคุณมีปัญหานี้ตรวจสอบให้แน่ใจว่าไม่มีสิ่งอื่นนอก sproc ที่กำลังสร้างธุรกรรม หากมีคุณอาจไม่สามารถใช้คำสั่ง return ภายใน sproc ได้เลย
EF0

นี่คือฉันฉันมีธุรกรรมและกำลังกลับมาก่อนที่ฉันจะทำธุรกรรมในคำสั่ง if / else
Kevin

19

โดยปกติจะเกิดขึ้นเมื่อธุรกรรมเริ่มต้นขึ้นและไม่มีข้อผูกมัดหรือไม่มีการย้อนกลับ

ในกรณีที่ข้อผิดพลาดเกิดขึ้นในขั้นตอนการจัดเก็บของคุณสิ่งนี้สามารถล็อกตารางฐานข้อมูลได้เนื่องจากธุรกรรมไม่เสร็จสมบูรณ์เนื่องจากข้อผิดพลาดรันไทม์บางอย่างในกรณีที่ไม่มีการจัดการข้อยกเว้นคุณสามารถใช้การจัดการข้อยกเว้นดังต่อไปนี้ ตั้งค่า XACT_ABORT

SET XACT_ABORT ON
SET NoCount ON
Begin Try 
     BEGIN TRANSACTION 
        //Insert ,update queries    
     COMMIT
End Try 
Begin Catch 
     ROLLBACK
End Catch

ที่มา


หากเป็นกรณีนี้คำถาม / คำตอบที่อ้างถึงน่าจะหมายถึงคำถามนี้ควรถูกทำเครื่องหมายว่าซ้ำและปิด
Mark Schultheiss

10

โปรดทราบว่าหากคุณใช้ธุรกรรมที่ซ้อนกันการดำเนินการ ROLLBACK จะย้อนกลับธุรกรรมที่ซ้อนกันทั้งหมดรวมถึงธุรกรรมที่อยู่นอกสุด

ซึ่งอาจเกิดจากการใช้งานร่วมกับ TRY / CATCH ทำให้เกิดข้อผิดพลาดที่คุณอธิบายไว้ ดูเพิ่มเติมได้ที่นี่


5

นอกจากนี้ยังสามารถเกิดขึ้นได้หากกระบวนงานที่จัดเก็บของคุณพบความล้มเหลวในการคอมไพล์หลังจากเปิดธุรกรรม (เช่นไม่พบตารางชื่อคอลัมน์ที่ไม่ถูกต้อง)

ฉันพบว่าฉันต้องใช้โพรซีเดอร์ที่เก็บไว้ 2 โพรซีเดอร์คือ "คนงาน" หนึ่งอันและหนึ่งห่อด้วยการลอง / จับทั้งสองด้วยตรรกะที่คล้ายกับที่ Remus Rusanu ระบุไว้ ตัวจับของผู้ปฏิบัติงานใช้เพื่อจัดการกับความล้มเหลว "ปกติ" และ Wrapper catch เพื่อจัดการข้อผิดพลาดในการคอมไพล์ล้มเหลว

https://msdn.microsoft.com/en-us/library/ms175976.aspx

ข้อผิดพลาดไม่ได้รับผลกระทบจาก TRY … CATCH Construct

ข้อผิดพลาดประเภทต่อไปนี้ไม่ได้รับการจัดการโดยบล็อก CATCH เมื่อเกิดขึ้นในระดับการดำเนินการเดียวกับโครงสร้าง TRY … CATCH:

  • ข้อผิดพลาดในการคอมไพล์เช่นข้อผิดพลาดทางไวยากรณ์ที่ทำให้ชุดงานไม่ทำงาน
  • ข้อผิดพลาดที่เกิดขึ้นระหว่างการคอมไพล์ใหม่ระดับคำสั่งเช่นข้อผิดพลาดในการแก้ปัญหาชื่ออ็อบเจ็กต์ที่เกิดขึ้นหลังจากการคอมไพล์เนื่องจากการแก้ปัญหาชื่อรอการตัดบัญชี

หวังว่าสิ่งนี้จะช่วยให้คนอื่นประหยัดเวลาในการดีบักได้สองสามชั่วโมง ...


1
ขอบคุณจัสติน การสังเกตที่ดี ในกรณีของฉันฉันกำลังทำการรวบรวมภายในการอัปเดตซึ่งไม่ก่อให้เกิดข้อผิดพลาดในการคอมไพล์ในระหว่างการบันทึก SP แต่เป็นไวยากรณ์ที่ไม่ถูกต้อง - "การรวมอาจไม่ปรากฏในรายการชุดคำสั่ง UPDATE"
kuklei

4

ในกรณีของฉันข้อผิดพลาดเกิดจากRETURNภายในไฟล์BEGIN TRANSACTION. ดังนั้นฉันจึงมีสิ่งนี้:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Return
 End
commit

และจะต้องเป็น:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Rollback Transaction ----- THIS WAS MISSING
     Return
 End
commit

2

สำหรับฉันหลังจากการดีบักอย่างกว้างขวางแล้วการแก้ไขก็เป็นการโยนที่ขาดหายไป คำสั่งในการจับหลังจากการย้อนกลับ หากไม่มีข้อความแสดงข้อผิดพลาดที่น่าเกลียดนี้คือสิ่งที่คุณพบ

begin catch
    if @@trancount > 0 rollback transaction;
    throw; --allows capture of useful info when an exception happens within the transaction
end catch

2

ฉันมีข้อความแสดงข้อผิดพลาดเดียวกันความผิดพลาดของฉันคือฉันมีเครื่องหมายอัฒภาคที่ท้ายบรรทัด COMMIT TRANSACTION


ง่ายๆเท่านี้. นอกจากนี้กรณีของฉันต้องการคำสั่ง 'ROLLBACK' ในกรณีที่ SP ไม่สามารถดำเนินการได้อย่างสมบูรณ์ เพียงเพื่อปิด / สิ้นสุดการทำธุรกรรม
J Cordero


1

ในความคิดของฉันคำตอบที่ได้รับการยอมรับในกรณีส่วนใหญ่คือ overkill

สาเหตุของข้อผิดพลาดมักไม่ตรงกันระหว่าง BEGIN และ COMMIT ตามที่ระบุไว้อย่างชัดเจนโดยข้อผิดพลาด ซึ่งหมายถึงการใช้:

Begin
  Begin
    -- your query here
  End
commit

แทน

Begin Transaction
  Begin
    -- your query here
  End
commit

การละเว้นธุรกรรมหลังจากเริ่มต้นทำให้เกิดข้อผิดพลาดนี้!


1

ตรวจสอบให้แน่ใจว่าคุณไม่มีธุรกรรมหลายรายการในขั้นตอน / แบบสอบถามเดียวกันซึ่งอย่างน้อยหนึ่งรายการถูกปล่อยทิ้งไว้โดยไม่ได้ตั้งใจ

ในกรณีของฉันฉันบังเอิญมีคำสั่ง BEGIN TRAN ในแบบสอบถาม


1

นอกจากนี้ยังขึ้นอยู่กับวิธีที่คุณเรียกใช้ SP จากรหัส C # ของคุณ ถ้า SP ส่งคืนค่าประเภทตารางบางประเภทให้เรียกใช้ SP ด้วย ExecuteStoreQuery และถ้า SP ไม่ส่งคืนค่าใด ๆ ให้เรียกใช้ SP ด้วย ExecuteStoreCommand



0

หากคุณมีโครงสร้างโค้ดของสิ่งที่ต้องการ:

SELECT 151
RETURN -151

จากนั้นใช้:

SELECT 151
ROLLBACK
RETURN -151
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.