การจัดการข้อยกเว้นในขั้นตอนการจัดเก็บที่เรียกว่าการใช้บล็อกแทรก-exec


10

ฉันมีขั้นตอนการจัดเก็บที่เรียกว่าในบล็อคแทรก-exec:

insert into @t
    exec('test')

ฉันจะจัดการกับข้อยกเว้นที่สร้างขึ้นในขั้นตอนการจัดเก็บและยังคงดำเนินการต่อได้อย่างไร

รหัสต่อไปนี้แสดงให้เห็นถึงปัญหา สิ่งที่ฉันต้องการจะทำคือคืน 0 หรือ -1 ขึ้นอยู่กับความสำเร็จหรือความล้มเหลวของการexec()โทรภายใน:

alter procedure test -- or create
as
begin try
    declare @retval int;
    -- This code assumes that PrintMax exists already so this generates an error
    exec('create procedure PrintMax as begin print ''hello world'' end;')
    set @retval = 0;
    select @retval;
    return(@retval);
end try
begin catch
    -- if @@TRANCOUNT > 0 commit;
    print ERROR_MESSAGE();
    set @retval = -1;
    select @retval;
    return(@retval);
end catch;
go

declare @t table (i int);

insert into @t
    exec('test');

select *
from @t;

return(-1)ปัญหาของฉันคือ เส้นทางสู่ความสำเร็จนั้นดี

ถ้าฉันปล่อยบล็อก try / catch ออกจากโพรซีเดอร์ที่เก็บไว้แสดงว่าเกิดข้อผิดพลาดและการแทรกล้มเหลว อย่างไรก็ตามสิ่งที่ฉันต้องการทำคือจัดการข้อผิดพลาดและคืนค่าที่ดี

โค้ดตามที่ส่งคืนข้อความ:

Msg 3930, Level 16, State 1, Line 6
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.

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

ถ้าฉันใส่เข้าไปif @@TRANCOUNT > 0ฉันก็จะได้รับข้อความ:

Msg 3916, Level 16, State 0, Procedure gordontest, Line 7
Cannot use the COMMIT statement within an INSERT-EXEC statement unless BEGIN TRANSACTION is used first.

ฉันได้ลองเล่นโดยเริ่มต้น / คอมมิชชันธุรกรรม แต่ดูเหมือนว่าไม่มีอะไรทำงาน

ดังนั้นฉันจะให้ขั้นตอนการจัดเก็บของฉันจัดการข้อผิดพลาดโดยไม่ยกเลิกการทำธุรกรรมโดยรวมได้อย่างไร

แก้ไขเพื่อตอบสนองต่อ Martin:

รหัสการโทรจริงคือ:

        declare @RetvalTable table (retval int);

        set @retval = -1;

        insert into @RetvalTable
            exec('

ประกาศ @ ย้อนกลับ int; exec @retval = '+ @ query +'; เลือก @retval ');

        select @retval = retval from @RetvalTable;

@queryการเรียกขั้นตอนการจัดเก็บอยู่ที่ไหน เป้าหมายคือการรับค่าตอบแทนจากขั้นตอนการจัดเก็บ หากเป็นไปได้โดยไม่ต้องinsert(หรือเฉพาะเจาะจงมากขึ้นโดยไม่ต้องเริ่มทำธุรกรรม) นั่นจะเป็นสิ่งที่ดี

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

if (@StoredProcedure = 'sp_rep__post') -- causing me a problem
begin
    exec @retval = sp_rep__post;
end;
else
begin
    -- the code I'm using now
end;

คุณกำลังพยายามแทรกอะไรลงในตัวแปรตาราง ค่าส่งคืนจะไม่ถูกแทรกเข้าไป declare @t table (i int);declare @RC int;exec @RC = test;insert into @t values (@RC);select * from @t;ทำงานได้ดี
Martin Smith

@MartinSmith . . วิธีที่รหัสใช้งานได้จริงจะเป็นอย่างไร select @retval; return @retvalในตอนท้าย หากคุณรู้วิธีอื่นในการรับค่าตอบแทนจากการเรียกขั้นตอนการจัดเก็บแบบไดนามิกฉันชอบที่จะรู้
Gordon Linoff

อีกวิธีหนึ่งก็คือDECLARE @RC INT;EXEC sp_executesql N'EXEC @RC = test', N'@RC INT OUTPUT', @RC = @RC OUTPUT;insert into @t VALUES (@RC)
Martin Smith

@MartinSmith . . ฉันคิดว่ามันจะได้ผล เราใช้เวลาครึ่งวันในการค้นหาความผิดพลาดของฮาร์ดแวร์ ("ไม่สามารถรองรับการดำเนินการที่เขียนไปยังล็อกไฟล์" เสียงเหมือนความล้มเหลวของฮาร์ดแวร์) และสองสามชั่วโมงที่ผ่านมาพยายามรับรหัสที่ถูกต้อง การทดแทนตัวแปรเป็นคำตอบที่ยอดเยี่ยม
Gordon Linoff

คำตอบ:


13

ข้อผิดพลาดในEXECส่วนของINSERT-EXECคำสั่งจะทำให้การทำธุรกรรมของคุณอยู่ในสถานะที่กำหนด

ถ้าคุณPRINTออกมาXACT_STATE()ในบล็อกมีการตั้งค่าCATCH-1

ข้อผิดพลาดบางอย่างจะตั้งค่าสถานะเป็นนี้ ข้อผิดพลาดการตรวจสอบข้อ จำกัด ต่อไปนี้ผ่านไปยัง catch catch และINSERTสำเร็จ

ALTER PROCEDURE test -- or create
AS
  BEGIN try
      DECLARE @retval INT;

      DECLARE @t TABLE(x INT CHECK (x = 0))

      INSERT INTO @t
      VALUES      (1)

      SET @retval = 0;

      SELECT @retval;

      RETURN( @retval );
  END try

  BEGIN catch
      PRINT XACT_STATE()

      PRINT ERROR_MESSAGE();

      SET @retval = -1;

      SELECT @retval;

      RETURN( @retval );
  END catch; 

เพิ่มสิ่งนี้ไปยังCATCHบล็อก

 IF (XACT_STATE()) = -1
BEGIN
    ROLLBACK TRANSACTION;
END;

ไม่ช่วย มันทำให้เกิดข้อผิดพลาด

ไม่สามารถใช้คำสั่ง ROLLBACK ภายในคำสั่ง INSERT-EXEC

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

DECLARE @RC INT;

EXEC sp_executesql
  N'EXEC @RC = test',
  N'@RC INT OUTPUT',
  @RC = @RC OUTPUT;

INSERT INTO @t
VALUES      (@RC) 

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

DECLARE @RetVal INT = -1

IF OBJECT_ID('PrintMax', 'P') IS NULL
  BEGIN
      EXEC('create procedure PrintMax as begin print ''hello world'' end;')

      SET @RetVal = 0
  END

SELECT @RetVal;

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