Xử lý các ngoại lệ trong các thủ tục được lưu trữ được gọi là sử dụng các khối insert-exec


10

Tôi có một thủ tục được lưu trữ được gọi trong khối insert-exec:

insert into @t
    exec('test')

Làm cách nào tôi có thể xử lý các ngoại lệ được tạo trong quy trình được lưu trữ và vẫn tiếp tục xử lý?

Các mã sau đây minh họa vấn đề. Những gì tôi muốn làm là trả về 0 hoặc -1 tùy thuộc vào thành công hay thất bại của exec()cuộc gọi nội bộ :

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;

Vấn đề của tôi là return(-1). Con đường thành công là tốt.

Nếu tôi bỏ qua khối thử / bắt trong quy trình được lưu trữ, thì lỗi sẽ xuất hiện và quá trình chèn không thành công. Tuy nhiên, điều tôi muốn làm là xử lý lỗi và trả về một giá trị đẹp.

Mã như trả về thông báo:

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.

Đây có lẽ là thông báo lỗi tồi tệ nhất mà tôi gặp phải. Nó dường như thực sự có nghĩa là "Bạn đã không xử lý một lỗi trong giao dịch lồng nhau."

Nếu tôi đặt vào if @@TRANCOUNT > 0, thì tôi nhận được tin nhắn:

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.

Tôi đã thử chơi xung quanh với các báo cáo giao dịch bắt đầu / cam kết, nhưng dường như không có gì hoạt động.

Vì vậy, làm cách nào để thủ tục lưu trữ của tôi xử lý lỗi mà không hủy bỏ giao dịch tổng thể?

Chỉnh sửa để đáp lại Martin:

Mã gọi thực tế là:

        declare @RetvalTable table (retval int);

        set @retval = -1;

        insert into @RetvalTable
            exec('

khai báo @retval int; exec @retval = '+ @ truy vấn +'; chọn @retval ');

        select @retval = retval from @RetvalTable;

Ở đâu @querylà cuộc gọi thủ tục lưu trữ. Mục tiêu là để có được giá trị trả về từ thủ tục được lưu trữ. Nếu điều này là có thể mà không có insert(hoặc cụ thể hơn là không bắt đầu giao dịch), điều đó sẽ rất tuyệt.

Tôi không thể sửa đổi các thủ tục được lưu trữ nói chung để lưu trữ giá trị trong một bảng, vì có quá nhiều trong số chúng. Một trong số đó là thất bại, và tôi có thể sửa đổi điều đó. Giải pháp tốt nhất hiện tại của tôi là một cái gì đó như:

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;

Bạn đang cố gắng chèn gì vào biến bảng? Giá trị trả lại không được chèn vào đó. declare @t table (i int);declare @RC int;exec @RC = test;insert into @t values (@RC);select * from @t;hoạt động tốt
Martin Smith

@MartinSmith. . . Cách mã thực sự hoạt động giống như select @retval; return @retvalở cuối. Nếu bạn biết một cách khác để nhận giá trị trả về từ một cuộc gọi thủ tục được lưu trữ động, tôi rất muốn biết.
Gordon Linoff

Một cách khác sẽ là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. . . Tôi nghĩ rằng sẽ làm việc. Chúng tôi đã dành nửa ngày để tìm kiếm lỗi phần cứng ("không thể hỗ trợ các hoạt động ghi vào tệp nhật ký" nghe có vẻ như lỗi phần cứng) và vài giờ qua cố gắng để có được mã đúng. Thay thế biến là một câu trả lời tuyệt vời.
Gordon Linoff

Câu trả lời:


13

Lỗi trong EXECmột phần của INSERT-EXECtuyên bố là khiến giao dịch của bạn ở trạng thái cam chịu.

Nếu bạn PRINTra ngoài XACT_STATE()trong CATCHkhối nó được thiết lập để -1.

Không phải tất cả các lỗi sẽ đặt trạng thái này. Các lỗi ràng buộc kiểm tra sau đây đi qua khối bắt và INSERTthành công.

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; 

Thêm phần này vào CATCHkhối

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

Không giúp được gì. Nó báo lỗi

Không thể sử dụng câu lệnh ROLLBACK trong câu lệnh INSERT-EXEC.

Tôi không nghĩ có bất kỳ cách nào để phục hồi từ một lỗi như vậy một khi nó đã xảy ra. Đối với trường hợp sử dụng cụ thể của bạn, INSERT ... EXECdù sao bạn không cần . Bạn có thể gán giá trị trả về cho một biến vô hướng sau đó chèn nó vào một câu lệnh riêng.

DECLARE @RC INT;

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

INSERT INTO @t
VALUES      (@RC) 

Hoặc tất nhiên bạn có thể cấu trúc lại thủ tục được lưu trữ để nó không gây ra lỗi đó.

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 ); 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.