SQL Server - การทำธุรกรรมย้อนกลับผิดพลาด?


193

เรามีแอปไคลเอนต์ที่ใช้งาน SQL บางตัวใน SQL Server 2005 ดังต่อไปนี้:

BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;

มันถูกส่งโดยคำสั่งสตริงยาวหนึ่งคำสั่ง

ถ้าส่วนใดส่วนหนึ่งล้มเหลวหรือส่วนใดส่วนหนึ่งของคำสั่งล้มเหลว SQL Server จะย้อนกลับธุรกรรมหรือไม่ หากไม่ย้อนกลับฉันต้องส่งคำสั่งที่สองเพื่อย้อนกลับหรือไม่

ฉันสามารถให้เฉพาะเจาะจงเกี่ยวกับ API และภาษาที่ฉันใช้ แต่ฉันคิดว่า SQL Server ควรตอบสนองเหมือนกันสำหรับภาษาใด ๆ


คำตอบ:


205

คุณสามารถใส่set xact_abort onก่อนการทำธุรกรรมของคุณเพื่อให้แน่ใจว่า sql ย้อนกลับโดยอัตโนมัติในกรณีที่มีข้อผิดพลาด


1
จะใช้งานกับ MS SQL 2K หรือสูงกว่าได้หรือไม่ ดูเหมือนว่าทางออกที่ง่ายที่สุด
jonathanpeppers

1
มันปรากฏในเอกสารสำหรับปี 2000, 2005 และ 2008 ดังนั้นฉันคิดว่าใช่ เราใช้มันในปี 2008

8
ฉันจำเป็นต้องปิดเครื่องหรือต่อเซสชันหนึ่งครั้งหรือไม่?
Marc

5
@ กำหนดขอบเขตของให้xact_abortอยู่ในระดับการเชื่อมต่อ
Keith

2
@AlexMcMillan คำสั่ง DROP PROCEDURE แก้ไขโครงสร้างฐานข้อมูลซึ่งแตกต่างจาก INSERT ซึ่งเพิ่งทำงานกับข้อมูล ดังนั้นจึงไม่สามารถห่อในธุรกรรม ฉันกำลังใช้คำพูดเกินจริง แต่โดยทั่วไปก็เป็นเช่นนั้น
eksortso

195

คุณถูกต้องในการทำธุรกรรมทั้งหมดที่จะถูกย้อนกลับ คุณควรออกคำสั่งเพื่อย้อนกลับ

คุณสามารถปิดสิ่งนี้ในTRY CATCHบล็อกดังนี้

BEGIN TRY
    BEGIN TRANSACTION

        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);

    COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN --RollBack in case of Error

    -- you can Raise ERROR with RAISEERROR() Statement including the details of the exception
    RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1)
END CATCH

2
ฉันชอบวิธีการแก้ปัญหาของ DyingCactus ที่ดีกว่าเขาเป็นโค้ด 1 บรรทัดที่จะเปลี่ยนแปลง หากคุณมีเหตุผลบางอย่างที่ดีกว่า (หรือเชื่อถือได้มากขึ้น) แจ้งให้เราทราบ
jonathanpeppers

14
ลองจับช่วยให้คุณสามารถจับ (และอาจแก้ไข) ข้อผิดพลาดและเพิ่มข้อผิดพลาดที่กำหนดเองหากจำเป็น
34832 Raj More

11
"บันทึกและบันทึก" บ่อยกว่า "บันทึกและแก้ไข" ฉันคิดว่า
quillbreaker

24
ไวยากรณ์ของ RAISERROR ไม่ถูกต้องอย่างน้อยใน SQL Server 2008R2 และใหม่กว่า ดูmsdn.microsoft.com/en-us/library/ms178592.aspxสำหรับไวยากรณ์ที่ถูกต้อง
Eric J.

2
@BornToCode เพื่อให้แน่ใจว่าการทำธุรกรรมมีอยู่ .. ให้บอกว่าคุณได้ย้อนกลับการทำธุรกรรมของคุณภายใต้เงื่อนไขที่กำหนด (ในtry), แต่รหัสล้มเหลวหลังจาก ไม่มีการทำธุรกรรมมากขึ้น catchแต่คุณยังคงที่จะเข้าสู่
Gabriel GM

42

นี่คือรหัสที่ได้รับข้อความแสดงข้อผิดพลาดการทำงานกับ MSSQL Server 2016:

BEGIN TRY
    BEGIN TRANSACTION 
        -- Do your stuff that might fail here
    COMMIT
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN

        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY()
        DECLARE @ErrorState INT = ERROR_STATE()

    -- Use RAISERROR inside the CATCH block to return error  
    -- information about the original error that caused  
    -- execution to jump to the CATCH block.  
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

1
ฉันต้องใช้DECLARE @Var TYPE; SET @Var = ERROR;เพื่อเพิ่มข้อผิดพลาดในเซิร์ฟเวอร์ sql 2005 มิฉะนั้นรหัสข้างต้นสำหรับการเพิ่มข้อผิดพลาดทำงานได้ดีกว่าฐานข้อมูลเก่าเกินไป การพยายามกำหนดค่าเริ่มต้นให้กับตัวแปรท้องถิ่นเป็นสาเหตุของปัญหา
jtlindsey

คุณสามารถใช้การโยนแบบง่าย แทนการประกาศ RAISERROR และ ERROR_ *
rodzmkii

21

จากบทความ MDSN, การทำธุรกรรมการควบคุม (โปรแกรมฐานข้อมูล)

หากเกิดข้อผิดพลาดคำสั่งรันไทม์ (เช่นการละเมิดข้อ จำกัด ) ในชุดการทำงานเริ่มต้นในโปรแกรมฐานข้อมูลคือการย้อนกลับเฉพาะคำสั่งที่สร้างข้อผิดพลาด คุณสามารถเปลี่ยนพฤติกรรมนี้ได้โดยใช้คำสั่ง SET XACT_ABORT หลังจากดำเนินการ SET XACT_ABORT ON ข้อผิดพลาดของคำสั่งรันไทม์ใด ๆ จะทำให้การย้อนกลับอัตโนมัติของธุรกรรมปัจจุบัน ข้อผิดพลาดในการคอมไพล์เช่นข้อผิดพลาดทางไวยากรณ์จะไม่ได้รับผลกระทบจาก SET XACT_ABORT สำหรับข้อมูลเพิ่มเติมดู SET XACT_ABORT (Transact-SQL)

ในกรณีของคุณมันจะย้อนกลับการทำธุรกรรมเสร็จสมบูรณ์เมื่อส่วนแทรกใด ๆ ล้มเหลว


3
เราต้องการอะไรในการจัดการข้อผิดพลาดทางไวยากรณ์? หรือรวบรวมข้อผิดพลาด? หากใครก็ตามที่เกิดขึ้นธุรกรรมทั้งหมดควรย้อนกลับมา
MonsterMMORPG

การรวบรวมข้อผิดพลาด / การรวบรวมไวยากรณ์เป็นสิ่งที่โครงการ SSDT ใช้ :-)
Joe the Coder

10

หากหนึ่งในส่วนแทรกล้มเหลวหรือส่วนใดส่วนหนึ่งของคำสั่งล้มเหลวเซิร์ฟเวอร์ SQL จะย้อนกลับธุรกรรมหรือไม่

ไม่มันไม่

หากไม่ย้อนกลับฉันต้องส่งคำสั่งที่สองเพื่อย้อนกลับหรือไม่

แน่นอนว่าคุณควรออก ROLLBACKCOMMITแทน

หากคุณต้องการตัดสินใจว่าจะยอมรับหรือย้อนกลับการทำธุรกรรมคุณควรลบCOMMITประโยคออกจากคำสั่งตรวจสอบผลลัพธ์ของส่วนแทรกจากนั้นออกอย่างใดอย่างหนึ่งCOMMITหรือROLLBACKขึ้นอยู่กับผลของการตรวจสอบ


ดังนั้นหากฉันได้รับข้อผิดพลาดพูดว่า "คีย์หลักขัดแย้งกัน" ฉันต้องส่งการโทรกลับครั้งที่สองเพื่อย้อนกลับหรือไม่ ฉันเดาว่ามันสมเหตุสมผล จะเกิดอะไรขึ้นหากมีข้อผิดพลาดเกี่ยวกับเครือข่ายเช่นการเชื่อมต่อถูกตัดระหว่างคำสั่ง SQL ที่ใช้เวลานานมาก?
jonathanpeppers

2
เมื่อการเชื่อมต่อหมดเวลาโปรโตคอลเครือข่ายพื้นฐาน (เช่นNamed PipesหรือTCP) จะหยุดการเชื่อมต่อ เมื่อการเชื่อมต่อถูกทำลายSQL Serverหยุดคำสั่งที่รันอยู่ในปัจจุบันทั้งหมดและย้อนกลับการทำธุรกรรม
Quassnoi

1
ดังนั้นโซลูชันของ DyingCactus ดูเหมือนจะแก้ไขปัญหาของฉันได้ขอบคุณสำหรับความช่วยเหลือ
jonathanpeppers

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