TL; DR / บทสรุปสำหรับผู้บริหาร:เกี่ยวกับส่วนนี้ของคำถาม:
ฉันไม่เห็นในสิ่งที่กรณีการควบคุมสามารถส่งผ่านภายในCATCHกับการทำธุรกรรมที่สามารถกระทำเมื่อXACT_ABORTONมีการตั้งค่า
ฉันได้ทำค่อนข้างบิตของการทดสอบเกี่ยวกับเรื่องนี้และตอนนี้ฉันไม่สามารถหากรณีใด ๆ ที่XACT_STATE()ผลตอบแทน1ภายในของCATCHบล็อกเมื่อ@@TRANCOUNT > 0 และเซสชั่นทรัพย์สินของผู้มีXACT_ABORT ONและตามความจริงแล้วตามหน้า MSDN ปัจจุบันของSET XACT_ABORT :
เมื่อ SET XACT_ABORT เปิดอยู่หากคำสั่ง Transact-SQL ทำให้เกิดข้อผิดพลาดรันไทม์ธุรกรรมทั้งหมดจะถูกยกเลิกและย้อนกลับ
คำแถลงนั้นดูเหมือนจะสอดคล้องกับการเก็งกำไรของคุณและการค้นพบของฉัน
บทความเกี่ยวกับ MSDN SET XACT_ABORTมีตัวอย่างเมื่อคำสั่งบางอย่างภายในธุรกรรมดำเนินการสำเร็จและบางอย่างล้มเหลวเมื่อXACT_ABORTตั้งค่าเป็นOFF
จริงแต่ข้อความในตัวอย่างนั้นไม่อยู่ในTRYบล็อก ข้อความเดียวกันเหล่านั้นภายในTRYบล็อกจะยังคงป้องกันการดำเนินการสำหรับข้อความใด ๆ หลังจากข้อความที่ทำให้เกิดข้อผิดพลาด แต่สมมติว่านั่นXACT_ABORTคือOFFเมื่อการควบคุมถูกส่งผ่านไปยังCATCHบล็อกธุรกรรมนั้นยังคงมีผลทางกายภาพในการเปลี่ยนแปลงก่อนหน้านี้ทั้งหมด และสามารถกระทำได้หากนั่นคือความปรารถนาหรือพวกเขาสามารถย้อนกลับ ในทางตรงกันข้ามถ้าXACT_ABORTเป็นONแล้วการเปลี่ยนแปลงใด ๆ ก่อนจะรีดกลับโดยอัตโนมัติและจากนั้นคุณจะได้รับเลือกอย่างใดอย่างหนึ่งดังนี้ก) ปัญหาROLLBACKซึ่งส่วนใหญ่เป็นเพียงการยอมรับสถานการณ์เนื่องจากธุรกรรมถูกย้อนกลับไปแล้วลบด้วยการรีเซ็ต@@TRANCOUNTเป็น0หรือ b) ได้รับข้อผิดพลาด มีตัวเลือกไม่มากใช่ไหม?
หนึ่งรายละเอียดที่สำคัญอาจจะเป็นจิ๊กซอว์ที่ไม่ได้ปรากฏในเอกสารว่าSET XACT_ABORTเป็นว่าทรัพย์สินเซสชั่นนี้และโค้ดตัวอย่างว่าแม้ได้รับรอบตั้งแต่SQL Server 2000 (เอกสารเกือบจะเหมือนกับระหว่างรุ่น) predating TRY...CATCHสร้างซึ่งเป็น นำมาใช้ใน SQL Server 2005 มองไปที่เอกสารนั้นอีกครั้งและกำลังมองหาตัวอย่าง ( โดยไม่ต้องTRY...CATCH ) โดยใช้XACT_ABORT ONทำให้เกิดการทันทีม้วนกลับมาของรายการ: ไม่มีสถานะการทำธุรกรรมของ "uncommittable" (โปรดทราบว่าไม่มีการกล่าวถึงที่ สถานะธุรกรรม "ไม่ยอมรับได้" ทั้งหมดในSET XACT_ABORTเอกสารประกอบนั้น)
ฉันคิดว่ามันสมเหตุสมผลที่จะสรุปว่า:
- บทนำของการ
TRY...CATCHสร้างใน SQL Server 2005 สร้างความต้องการสำหรับสถานะธุรกรรมใหม่ (เช่น "uncommittable") และXACT_STATE()ฟังก์ชันเพื่อรับข้อมูลนั้น
- การตรวจสอบ
XACT_STATE()ในCATCHบล็อกจริง ๆ จะสมเหตุสมผลถ้าทั้งสองอย่างต่อไปนี้เป็นจริง:
XACT_ABORTคือOFF(อื่นXACT_STATE()ควรกลับมา-1และ@@TRANCOUNTจะเป็นสิ่งที่คุณต้องการ)
- คุณมีเหตุผลใน
CATCHบล็อกหรือบางแห่งขึ้นห่วงโซ่ถ้าสายจะซ้อนกันที่ทำให้มีการเปลี่ยนแปลง (กCOMMITหรือแม้กระทั่งดราก้อนใด ๆ DDL ฯลฯ คำสั่ง) แทนการROLLBACKทำ (นี่เป็นกรณีการใช้งานที่ผิดปกติมาก) ** โปรดดูหมายเหตุที่ด้านล่างในส่วน UPDATE 3 เกี่ยวกับคำแนะนำที่ไม่เป็นทางการโดย Microsoft เพื่อตรวจสอบเสมอXACT_STATE()แทนที่จะเป็น@@TRANCOUNTและทำไมการทดสอบแสดงให้เห็นว่าเหตุผลของพวกเขาไม่ดี
- บทนำของการ
TRY...CATCHสร้างใน SQL Server 2005 มีส่วนใหญ่ล้าสมัยXACT_ABORT ONคุณสมบัติเซสชั่นในขณะที่มันให้การควบคุมระดับสูงกว่าการทำธุรกรรม (อย่างน้อยคุณมีตัวเลือกให้COMMITโดยที่XACT_STATE()ไม่ได้กลับมา-1)
วิธีการดูที่นี้ก็คือก่อนที่จะ SQL Server 2005 , XACT_ABORT ONจัดให้มีวิธีที่ง่ายและเชื่อถือได้ในการประมวลผลหยุดเมื่อมีข้อผิดพลาดเกิดขึ้นเมื่อเทียบกับการตรวจสอบ@@ERRORหลังจากแต่ละคำสั่ง
- ตัวอย่างรหัสเอกสารสำหรับ
XACT_STATE()เป็นที่ผิดพลาดหรือที่ดีที่สุดที่ทำให้เข้าใจผิดในการที่จะแสดงให้เห็นว่าการตรวจสอบXACT_STATE() = 1เมื่อเป็นXACT_ABORTON
ส่วนที่ยาว ;-)
ใช่รหัสตัวอย่างใน MSDN นั้นค่อนข้างสับสน (โปรดดู: @@ TRANCOUNT (Rollback) กับ XACT_STATE ) ;-) และฉันรู้สึกว่ามันทำให้เข้าใจผิดเพราะอาจแสดงบางสิ่งที่ไม่สมเหตุสมผล (ด้วยเหตุผลที่คุณถาม: คุณสามารถมีธุรกรรม "committable" ในCATCHบล็อกเมื่อXACT_ABORTมีON) หรือแม้ว่าจะเป็นไปได้ก็ตาม ยังคงเน้นไปที่ความเป็นไปได้ทางเทคนิคที่มีน้อยคนที่จะต้องการหรือต้องการและไม่สนใจเหตุผลที่มีแนวโน้มว่าจะต้องการ
หากมีข้อผิดพลาดรุนแรงพอในบล็อก TRY ตัวควบคุมจะผ่านเข้าสู่การจับ ดังนั้นถ้าฉันอยู่ใน CATCH ฉันรู้ว่าธุรกรรมมีปัญหาและสิ่งเดียวที่สมเหตุสมผลที่จะทำในกรณีนี้คือการย้อนกลับใช่มั้ย
ฉันคิดว่ามันจะช่วยได้ถ้าเราทำให้แน่ใจว่าเราอยู่ในหน้าเดียวกันเกี่ยวกับความหมายของคำและแนวคิด:
"ข้อผิดพลาดรุนแรงพอ":เพื่อให้ชัดเจนTRY ... CATCHจะดักข้อผิดพลาดส่วนใหญ่ รายการสิ่งที่จะไม่ถูกจับปรากฏในหน้า MSDN ที่เชื่อมโยงนั้นภายใต้ส่วน "ข้อผิดพลาดที่ไม่ได้รับผลกระทบจาก TRY … CATCH Construct"
"ถ้าฉันอยู่ใน CATCH ฉันรู้ว่าการทำธุรกรรมมีปัญหา" ( เพิ่มphas em ):หากโดย "transaction" คุณหมายถึงหน่วยทางตรรกะของงานตามที่คุณกำหนดโดยการจัดกลุ่มคำสั่งในการทำธุรกรรมอย่างชัดเจนจากนั้น มีโอกาสมากที่สุดใช่ ฉันคิดว่าพวกเราส่วนใหญ่ฐานข้อมูล DB มักจะเห็นด้วยว่าการย้อนกลับคือ "สิ่งที่สมเหตุสมผลเท่านั้นที่จะทำ" เนื่องจากเรามีมุมมองที่คล้ายกันเกี่ยวกับวิธีการและเหตุผลที่เราใช้ธุรกรรมที่ชัดเจนและคิดว่าขั้นตอนใดควรเป็นหน่วยอะตอม ของการทำงาน.
แต่ถ้าคุณหมายถึงหน่วยงานที่แท้จริงที่ถูกจัดกลุ่มไว้ในธุรกรรมที่ชัดเจนดังนั้นไม่คุณไม่ทราบว่าตัวธุรกรรมนั้นมีปัญหา คุณจะรู้ว่างบดำเนินการในการทำธุรกรรมที่กำหนดไว้อย่างชัดเจนได้ยกข้อผิดพลาด แต่อาจไม่ใช่คำสั่ง DML หรือ DDL และแม้ว่ามันจะเป็นคำสั่ง DML ธุรกรรมเองก็อาจจะยังสามารถตกลงกันได้
จากสองประเด็นที่กล่าวมาข้างต้นเราอาจต้องแยกความแตกต่างระหว่างการทำธุรกรรมที่คุณ "ไม่สามารถ" ยอมรับได้และรายการที่คุณ "ไม่ต้องการ" ที่จะส่งมอบ
เมื่อXACT_STATE()ส่งกลับ1นั่นหมายความว่าธุรกรรมดังกล่าวคือ "committable" ที่คุณมีทางเลือกระหว่างหรือCOMMIT ROLLBACKคุณอาจไม่ต้องการที่จะยอมรับมัน แต่ถ้าสำหรับบางเรื่องที่ยากที่จะเกิดขึ้นด้วยเหตุผลที่คุณต้องการอย่างน้อยคุณก็ทำได้เพราะบางส่วนของการทำธุรกรรมเสร็จสมบูรณ์
แต่เมื่อXACT_STATE()ส่งกลับค่า a -1คุณต้องทำจริง ๆROLLBACKเพราะส่วนหนึ่งของธุรกรรมกลายเป็นสถานะที่ไม่ดี ตอนนี้ฉันเห็นด้วยว่าถ้าการควบคุมได้ถูกส่งไปยัง CATCH block มันก็สมเหตุสมผลแล้วที่จะตรวจสอบ@@TRANCOUNTเพราะแม้ว่าคุณจะสามารถทำธุรกรรมได้ทำไมคุณถึงต้องการ?
แต่ถ้าคุณสังเกตที่ด้านบนของตัวอย่างการตั้งค่าของXACT_ABORT ONการเปลี่ยนแปลงสิ่งเล็กน้อย คุณสามารถมีข้อผิดพลาดปกติหลังจากที่ทำBEGIN TRANว่าจะผ่านการควบคุมไปยังบล็อกที่จับได้เมื่อXACT_ABORTเป็นOFFและ XACT_STATE () 1จะกลับมา แต่ถ้า XACT_ABORT เป็นONแล้วทำรายการคือ "ยกเลิก" (คือทำให้เป็นโมฆะ) สำหรับ 'OL ข้อผิดพลาดใด ๆ แล้วจะกลับมาXACT_STATE() -1ในกรณีนี้ดูเหมือนว่าไร้ประโยชน์ในการตรวจสอบXACT_STATE()ภายในCATCHบล็อกเป็นมันก็ดูเหมือนว่าจะกลับ-1เมื่อเป็นXACT_ABORTON
ดังนั้นแล้วมีXACT_STATE()ไว้เพื่ออะไร เบาะแสบางอย่างคือ:
หน้า MSDN สำหรับTRY...CATCHภายใต้ส่วน "ธุรกรรมที่ไม่สามารถทำได้และ XACT_STATE" กล่าวว่า:
ข้อผิดพลาดที่มักจะจบการทำธุรกรรมนอกบล็อกลองทำธุรกรรมที่จะเข้าสู่สถานะที่ไม่สามารถยอมรับได้เมื่อเกิดข้อผิดพลาดภายในบล็อกลอง
หน้า MSDN สำหรับSET XACT_ABORTภายใต้ส่วน "หมายเหตุ" กล่าวว่า:
เมื่อ SET XACT_ABORT ปิดในบางกรณีเฉพาะคำสั่ง Transact-SQL ที่ยกข้อผิดพลาดจะถูกย้อนกลับและธุรกรรมดำเนินการต่อไป
และ:
XACT_ABORT ต้องตั้งค่า ON สำหรับคำสั่งการปรับเปลี่ยนข้อมูลในธุรกรรมโดยนัยหรือชัดเจนกับผู้ให้บริการ OLE DB ส่วนใหญ่รวมถึง SQL Server
หน้า MSDN สำหรับBEGIN TRANSACTIONในส่วน "หมายเหตุ" กล่าวว่า:
ธุรกรรมในพื้นที่ที่เริ่มต้นโดยคำสั่ง BEGIN TRANSACTION จะถูกยกระดับไปยังธุรกรรมแบบกระจายหากมีการดำเนินการต่อไปนี้ก่อนที่คำสั่งจะถูกส่งมอบหรือย้อนกลับ:
- คำสั่ง INSERT, DELETE หรือ UPDATE ที่อ้างถึงตารางระยะไกลบนเซิร์ฟเวอร์ที่เชื่อมโยงจะถูกดำเนินการ คำสั่ง INSERT, UPDATE หรือ DELETE ล้มเหลวหากผู้ให้บริการ OLE DB ที่ใช้ในการเข้าถึงเซิร์ฟเวอร์ที่เชื่อมโยงไม่รองรับส่วนติดต่อ ITransactionJoin
ดูเหมือนว่าการใช้งานที่เหมาะสมที่สุดจะอยู่ในบริบทของคำสั่ง DML ของเซิร์ฟเวอร์ที่เชื่อมโยง และฉันเชื่อว่าฉันวิ่งเข้าไปในนี้เองเมื่อหลายปีก่อน ฉันจำรายละเอียดทั้งหมดไม่ได้ แต่มีบางอย่างที่เกี่ยวข้องกับเซิร์ฟเวอร์ระยะไกลที่ไม่พร้อมใช้งานและด้วยเหตุผลบางอย่างข้อผิดพลาดนั้นไม่ได้ติดอยู่ในบล็อคลองและไม่เคยถูกส่งไปที่ CATCH เลย COMMIT เมื่อไม่ควรมี แน่นอนว่าอาจเป็นปัญหาของการไม่ได้XACT_ABORTตั้งค่าONแทนที่จะล้มเหลวในการตรวจสอบXACT_STATE()หรืออาจเป็นได้ทั้ง และฉันจำได้ว่ากำลังอ่านสิ่งที่กล่าวว่าถ้าคุณใช้เซิร์ฟเวอร์ที่เชื่อมโยงและ / หรือธุรกรรมที่ถูกแจกจ่ายคุณจำเป็นต้องใช้XACT_ABORT ONและ / หรือXACT_STATE()แต่ฉันไม่สามารถหาเอกสารนั้นได้ในตอนนี้ หากฉันพบฉันจะอัปเดตด้วยลิงค์
แต่ฉันก็ยังมีความพยายามหลายสิ่งหลายอย่างและฉันไม่สามารถหาสถานการณ์ที่มีXACT_ABORT ONและผ่านการควบคุมไปยังCATCHบล็อกที่มีการรายงานXACT_STATE()1
ลองตัวอย่างเหล่านี้เพื่อดูผลกระทบของXACT_ABORTมูลค่า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;
UPDATE
แม้ว่าจะไม่ได้เป็นส่วนหนึ่งของคำถามต้นฉบับโดยอ้างอิงจากความคิดเห็นเหล่านี้ในคำตอบนี้:
ฉันได้อ่านผ่านบทความ Erland ในข้อผิดพลาดและการจัดการการทำธุรกรรมที่เขาบอกว่าXACT_ABORTเป็นไปโดยปริยายด้วยเหตุผลเดิมและปกติเราควรจะตั้งค่าให้OFF
...
"... ถ้าคุณทำตามคำแนะนำแล้วเปิดด้วย SET XACT_ABORT ON ธุรกรรมจะถูกทำซ้ำ"ON
ก่อนที่จะใช้XACT_ABORT ONทุกที่ฉันจะถามว่า: สิ่งที่ได้รับที่นี่? ฉันไม่พบว่าจำเป็นต้องทำและสนับสนุนโดยทั่วไปว่าคุณควรใช้เมื่อจำเป็นเท่านั้น ไม่ว่าคุณต้องการROLLBACKจะจัดการได้อย่างง่ายดายเพียงพอหรือไม่โดยใช้เทมเพลตที่แสดงในคำตอบของ @ Remus หรือสิ่งที่ฉันใช้มานานหลายปีซึ่งเป็นสิ่งเดียวกัน แต่ไม่มีจุดบันทึกตามที่แสดงในคำตอบนี้ (ซึ่ง จัดการสายซ้อนกัน):
เราจำเป็นต้องจัดการธุรกรรมในรหัส C # หรือในขั้นตอนการจัดเก็บ
อัพเดท 2
ฉันทำการทดสอบเพิ่มอีกเล็กน้อยคราวนี้ด้วยการสร้าง. NET Console แอปขนาดเล็กสร้างธุรกรรมในเลเยอร์แอปก่อนที่จะดำเนินการSqlCommandวัตถุใด ๆ(เช่นผ่านusing (SqlTransaction _Tran = _Connection.BeginTransaction()) { ...) รวมถึงการใช้ข้อผิดพลาดแบบแบทช์แทนคำสั่งเพียงอย่างเดียว ข้อผิดพลาดในการจัดกลุ่มและพบว่า:
- ธุรกรรม "ธรรมดา" เป็นรายการที่ถูกย้อนกลับไปแล้วส่วนใหญ่แล้ว (การเปลี่ยนแปลงถูกยกเลิกไปแล้ว) แต่
@@TRANCOUNTยังคงเป็น> 0
- เมื่อคุณมีการทำธุรกรรม "uncommitable" คุณจะไม่สามารถออกจากการ
COMMITที่จะสร้างและข้อผิดพลาดที่บอกว่าการทำธุรกรรมเป็น "uncommittable" คุณยังไม่สามารถเพิกเฉยได้ / ไม่ทำอะไรเลยเนื่องจากข้อผิดพลาดจะถูกสร้างขึ้นเมื่อแบทช์เสร็จสิ้นโดยระบุว่าแบทช์เสร็จสมบูรณ์ด้วยธุรกรรมที่เอ้อระเหยไม่สามารถยอมรับได้และจะถูกย้อนกลับ (ดังนั้นอืมถ้ามันจะย้อนกลับอัตโนมัติ ทำไมต้องทิ้งข้อผิดพลาด?) ดังนั้นคุณต้องออกอย่างชัดเจนROLLBACKอาจไม่ได้อยู่ในCATCHบล็อกทันทีแต่ก่อนที่แบทช์จะสิ้นสุด
- ในการ
TRY...CATCHสร้างเมื่อXACT_ABORTมีOFFข้อผิดพลาดที่จะยุติการทำธุรกรรมโดยอัตโนมัติหากพวกเขาเกิดขึ้นนอกTRYบล็อกเช่นข้อผิดพลาดแบทช์ยกเลิกจะยกเลิกการทำงาน แต่ไม่ยุติ Tranasction ปล่อยให้มันเป็น "uncommitable" การออก a ROLLBACKเป็นวิธีที่จำเป็นมากกว่าในการปิดธุรกรรม แต่งานได้ย้อนกลับไปแล้ว
- เมื่อ
XACT_ABORTใดONข้อผิดพลาดส่วนใหญ่จะทำหน้าที่ยกเลิกการแบทช์และจะทำงานตามที่อธิบายไว้ในหัวข้อย่อยด้านบนโดยตรง (# 3)
XACT_STATE()อย่างน้อยในCATCHบล็อกจะแสดง-1ข้อผิดพลาดสำหรับการยกเลิกแบทช์หากมีธุรกรรมที่ใช้งานอยู่ในเวลาที่เกิดข้อผิดพลาด
XACT_STATE()บางครั้งจะส่งคืน1แม้ว่าจะไม่มีธุรกรรมที่ใช้งานอยู่ ถ้า@@SPID(อื่น) อยู่ในSELECTรายการพร้อมกับXACT_STATE()แล้วXACT_STATE()จะกลับ 1 เมื่อไม่มีการทำธุรกรรมที่ใช้งาน พฤติกรรมนี้เริ่มต้นใน SQL Server 2012 และมีอยู่ในปี 2014 แต่ฉันไม่ได้ทดสอบในปี 2559
ด้วยจุดสำคัญในใจ:
- จุดที่ได้รับ # 4 และ # 5 เนื่องจากส่วนใหญ่ (หรือทั้งหมด?) ข้อผิดพลาดจะทำให้การทำธุรกรรม "uncommitable" มันดูเหมือนไม่มีจุดหมายทั้งหมดเพื่อตรวจสอบ
XACT_STATE()ในCATCHบล็อกเมื่อXACT_ABORTเป็นตั้งแต่ค่าที่ส่งกลับจะเป็นON-1
- การตรวจสอบ
XACT_STATE()ในCATCHบล็อกเมื่อXACT_ABORTใดที่OFFเหมาะสมกว่าเนื่องจากค่าส่งคืนจะมีการเปลี่ยนแปลงอย่างน้อยเนื่องจากจะส่งคืน1ข้อผิดพลาดการยกเลิกคำสั่ง อย่างไรก็ตามหากคุณเขียนโค้ดเหมือนกับพวกเราส่วนใหญ่ความแตกต่างนี้ไม่มีความหมายเนื่องจากคุณจะโทรROLLBACKไปหาคุณเพียงเพราะความจริงที่ว่ามีข้อผิดพลาดเกิดขึ้น
- หากคุณพบว่าสถานการณ์ที่ไม่ใบสำคัญแสดงสิทธิที่ออกให้
COMMITในCATCHบล็อกแล้วตรวจสอบค่าของและให้แน่ใจว่าXACT_STATE()SET XACT_ABORT OFF;
XACT_ABORT ONดูเหมือนว่าจะให้ประโยชน์เล็กน้อยกับไม่มีTRY...CATCHโครงสร้าง
- ฉันสามารถหาสถานการณ์ที่ไม่มีการตรวจสอบให้เป็นประโยชน์มีความหมายมากกว่าเพียงแค่การตรวจสอบ
XACT_STATE()@@TRANCOUNT
- ฉันยังสามารถพบสถานการณ์ที่ไม่มี
XACT_STATE()ผลตอบแทน1ในCATCHบล็อกเมื่อเป็นXACT_ABORT ONฉันคิดว่ามันเป็นข้อผิดพลาดของเอกสาร
- ใช่คุณสามารถย้อนกลับธุรกรรมที่คุณไม่ได้เริ่มต้นอย่างชัดเจน และในบริบทของการใช้
XACT_ABORT ONเป็นจุดที่สงสัยเนื่องจากข้อผิดพลาดที่เกิดขึ้นในTRYบล็อกจะย้อนกลับการเปลี่ยนแปลงโดยอัตโนมัติ
- การ
TRY...CATCHสร้างมีผลประโยชน์มากกว่าXACT_ABORT ONในการไม่ยกเลิกการทำธุรกรรมทั้งหมดโดยอัตโนมัติดังนั้นจึงอนุญาตให้ทำธุรกรรม (ตราบใดที่มีXACT_STATE()ผลตอบแทน1) ที่จะผูกพัน(แม้ว่าจะเป็นกรณีที่มีขอบ)
ตัวอย่างของการXACT_STATE()กลับมา-1เมื่อXACT_ABORTเป็น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;
อัพเดท 3
เกี่ยวข้องกับรายการ # 6 ในส่วน UPDATE 2 (เช่นค่าที่เป็นไปได้ที่ไม่ถูกต้องถูกส่งคืนโดยXACT_STATE()เมื่อไม่มีธุรกรรมที่ใช้งานอยู่):
- ลักษณะการทำงานผิดปกติ / ผิดพลาดเริ่มขึ้นใน SQL Server 2012 (จนถึงขณะนี้ได้รับการทดสอบกับ 2012 SP2 และ 2014 SP1)
- ใน SQL Server รุ่น 2005, 2008, และ 2008 R2
XACT_STATE()ไม่ได้รายงานค่าที่คาดไว้เมื่อใช้ในทริกเกอร์หรือINSERT...EXECสถานการณ์: xact_state () ไม่สามารถใช้อย่างเชื่อถือได้เพื่อตรวจสอบว่ามีการทำธุรกรรมอีกต่อไปหรือไม่ อย่างไรก็ตามในทั้ง 3 รุ่น (ผมทดสอบเฉพาะใน 2008 R2) XACT_STATE()ไม่ได้ไม่ถูกต้องรายงาน1เมื่อใช้ในด้วยSELECT@@SPID
มีข้อผิดพลาดการเชื่อมต่อฟ้องพฤติกรรมที่กล่าวถึงนี่ แต่ถูกปิดเป็น "โดยการออกแบบ": XACT_STATE () สามารถกลับมาเป็นรัฐทำธุรกรรมที่ไม่ถูกต้องใน SQL 2012 อย่างไรก็ตามการทดสอบเสร็จสิ้นเมื่อเลือกจาก DMV และได้ข้อสรุปว่าการทำเช่นนั้นจะมีระบบสร้างธุรกรรมอย่างน้อยที่สุดสำหรับ DMV บางตัว มันก็ยังระบุไว้ในการตอบสนองสุดท้ายโดย MS ว่า:
โปรดทราบว่าคำสั่ง IF และ SELECT ที่ไม่มีจาก FROM ห้ามเริ่มทำธุรกรรม
ตัวอย่างเช่นการเรียกใช้ SELECT XACT_STATE () หากคุณไม่มีธุรกรรมที่มีอยู่ก่อนหน้านี้จะคืนค่า 0
ข้อความเหล่านั้นไม่ถูกต้องตามตัวอย่างต่อไปนี้:
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
ดังนั้นข้อผิดพลาดการเชื่อมต่อใหม่:
XACT_STATE () ส่งคืน 1 เมื่อใช้ใน SELECT พร้อมกับตัวแปรระบบบางส่วน แต่ไม่มี FROM clause
โปรดทราบว่าใน "XACT_STATE () สามารถส่งคืนสถานะการทำธุรกรรมที่ไม่ถูกต้องใน SQL 2012" รายการเชื่อมต่อที่เชื่อมโยงโดยตรงด้านบนสถานะ Microsoft (ดีตัวแทนของ):
@@ trancount ส่งคืนจำนวนคำสั่ง BEGIN TRAN ดังนั้นจึงไม่ใช่ตัวบ่งชี้ที่เชื่อถือได้ว่ามีธุรกรรมที่ใช้งานอยู่หรือไม่ XACT_STATE () จะส่งคืน 1 หากมีธุรกรรม autocommit ที่ใช้งานอยู่และเป็นตัวบ่งชี้ที่เชื่อถือได้มากขึ้นว่ามีธุรกรรมที่ใช้งานอยู่หรือไม่
อย่างไรก็ตามฉันไม่สามารถหาเหตุผลที่จะไม่ไว้วางใจ@@TRANCOUNTได้ การทดสอบต่อไปนี้แสดงให้เห็นว่า@@TRANCOUNTจะส่งคืน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
ฉันยังทดสอบบนตารางจริงด้วย Trigger และ@@TRANCOUNTภายใน Trigger ได้รายงานอย่างถูกต้อง1แม้ว่าจะไม่มีการทำธุรกรรมที่ชัดเจน
XACT_ABORTไปหรือONOFF