insert-execブロックを使用して呼び出されたストアドプロシージャでの例外の処理


10

insert-execブロックで呼び出されるストアドプロシージャがあります。

insert into @t
    exec('test')

ストアドプロシージャで生成された例外を処理し、処理を続行するにはどうすればよいですか?

次のコードは問題を示しています。私がやりたいことは、内部exec()呼び出しの成功または失敗に応じて0または-1を返すことです。

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.

begin / commitトランザクションステートメントをいじってみましたが、何も機能しないようです。

では、トランザクション全体を中止せずにストアドプロシージャでエラーを処理するにはどうすればよいですか?

マーティンに応じて編集します。

実際の呼び出しコードは次のとおりです。

        declare @RetvalTable table (retval int);

        set @retval = -1;

        insert into @RetvalTable
            exec('

@retval intを宣言します。exec @retval = '+ @ query +'; select @retval ');

        select @retval = retval from @RetvalTable;

@queryストアドプロシージャコールはどこにありますか。目的は、ストアドプロシージャから戻り値を取得することです。これがinsert(トランザクションを開始せずに)なくても可能であれば、それは素晴らしいことです。

値が多すぎるため、一般にストアドプロシージャを変更して値をテーブルに格納できません。 そのうちの1つが失敗していて、私はそれを修正できます。私の現在の最善の解決策は次のようなものです:

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;正常に動作します。
マーティン・スミス

@MartinSmith。。。コードが実際に機能する方法はselect @retval; return @retval、最後の方に似 ています。動的なストアドプロシージャの呼び出しから戻り値を取得する別の方法を知っている場合は、知りたいと思います。
Gordon Linoff 2013

さてもう一つの方法は次のようになりますDECLARE @RC INT;EXEC sp_executesql N'EXEC @RC = test', N'@RC INT OUTPUT', @RC = @RC OUTPUT;insert into @t VALUES (@RC)
マーティン・スミス

@MartinSmith。。。それでうまくいくと思います。半日かけてハードウェアの障害( "ログファイルに書き込む操作をサポートできない"とはハードウェアの障害のように聞こえる)を探して、過去数時間、コードを正しく取得しようとしました。変数置換は優れた答えです。
ゴードンリノフ2013

回答:


13

ステートメントのEXEC一部にエラーがありINSERT-EXEC、トランザクションが破壊された状態になっています。

もしあればPRINTアウトXACT_STATE()CATCHそれが設定されているブロック-1

すべてのエラーが状態をこれに設定するわけではありません。次のチェック制約エラーは、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;

助けにはならない。エラーが出ます

INSERT-EXECステートメント内ではROLLBACKステートメントを使用できません。

一度発生したエラーから回復する方法はないと思います。特定のユースケースでは、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.