web-dev-qa-db-ja.com

複数のストアドプロシージャを順番に実行する

T-SQL(SQL Server 2008 R2)で記述されたストアドプロシージャがあります。これは、基本的に他の複数のサブプロシージャを順番に呼び出すマスタープロシージャです。呼び出しとエラー処理は、プロシージャの名前を除いて、それぞれ同じです。

OO言語では、インターフェイスやファンクタなどの抽象化を使用し、一連のオブジェクトをループします。SQLでは機能しませんが、このコードを作成する方法を見つけたいと思いますコピーと貼り付けの繰り返しを減らしてより簡潔にします。はい、基本的に SQLは集合演算に関するものです であり、私がやりたいことをサポートしていませんが、方法がある場合は、また、各ストアドプロシージャの呼び出しの結果をキャプチャして、この質問には関係のない処理を行う必要もあります。

ここに私がこれまでに持っているものがあります:

CREATE PROCEDURE dbo.testproc
AS BEGIN
  DECLARE @step INT, @result INT
  DECLARE @tbl TABLE([step] INT, [pname] NVARCHAR(40))

  INSERT INTO @tbl ([step], [pname]) VALUES (1, N'proc1')
  INSERT INTO @tbl ([step], [pname]) VALUES (2, N'proc2')
  INSERT INTO @tbl ([step], [pname]) VALUES (3, N'proc3')
  -- Potentially many more procedures here

  SET @step = 1
  WHILE @step <= (SELECT MAX([step]) FROM @tbl)
  BEGIN
    DECLARE @sql NVARCHAR(60)
    SET @sql = N'EXEC @result = dbo.' + (SELECT [pname] FROM @tbl WHERE [step] = @step)
    EXEC (@sql)
    IF @result <> 0
    BEGIN
      INSERT INTO SomeTable error code and step number
      RETURN
    END
   SET @step = @step + 1
  END
END
GO

プロシージャを実行すると、動的SQLの一部である@result変数が@sql変数に含まれるバッチの一部として定義されていないため、SQL Serverからエラーが返されます。このように変更すると:

    SET @sql = N'EXEC dbo.' + (SELECT [pname] FROM @tbl WHERE [step] = @step)
    EXEC @result = (@sql)

構文エラーが発生します。

これは、サブプロシージャの戻り値を取得する場合を除いて、正常に機能します。私の表明した目標を達成する方法はありますか。

注:私がここで尋ねたことに基づいて、特にテーブル変数を指定すると、WHILEループよりも カーソルはより良い実装のように聞こえます です。この質問に必須ではないコードの一部には、反復数の把握が含まれているため、ループ制御変数を使用しています。

5
user48112

後で結果の値が必要ない場合は、この方法で短くすることができます。

-- procedures to test with
create proc proc1 as print '1' return 0
GO
create proc proc2 as print '2' return 1
GO
create proc proc3 as print '3' return 0
GO

if object_id('dbo.testproc') is null exec('create procedure dbo.testproc as return(0)')
GO
alter PROCEDURE dbo.testproc
AS 
  DECLARE @result INT
        , @sql nvarchar(max) = N''
  DECLARE @tbl TABLE([step] INT, [pname] nvarchar(513))

  INSERT INTO @tbl ([step], [pname]) 
  VALUES (1, N'proc1'), 
         (2, N'proc2'),
         (3, N'proc3')
  -- Potentially many more procedures here

  select @sql = @sql + 'exec @result = ' + QUOTENAME(pname) + ' if @result <> 0 return;'
  from @tbl order by step

  exec sp_executesql @sql, N'@result int output', @result output

  if @result <> 0
    begin
      print 'do your cleanup'
    end 
GO

exec testproc

変数エンジンは次のクエリバッチを生成します

exec @result = [proc1] if @result <> 0 return;
exec @result = [proc2] if @result <> 0 return;
exec @result = [proc3] if @result <> 0 return;

バッチが実行されると、@ resultがゼロではないときに実行が停止し、その値が出力パラメーターに保持されます。

より伝統的なループ

手順をループしたい場合。パラメータがない(またはパラメータがすべて同じ)ので、exec @result = @procを呼び出すだけです。

if object_id('dbo.testproc') is null exec('create procedure dbo.testproc as return(0)')
GO
alter PROCEDURE dbo.testproc
AS 
  DECLARE @result INT
        , @proc sysname
  DECLARE @tbl TABLE([step] INT, [pname] nvarchar(513))

  INSERT INTO @tbl ([step], [pname]) 
  VALUES (1, N'proc1'), 
         (2, N'proc2'),
         (3, N'proc3')
  -- Potentially many more procedures here

  declare c cursor fast_forward local
  for select pname from @tbl order by step

  open c
  fetch next from c into @proc
  while @@FETCH_STATUS = 0
    begin 
      exec @result = @proc 
      if @result <> 0
          BREAK

      fetch next from c into @proc
    end
  close c
  deallocate c

  if @result <> 0
    begin
      print 'do your cleanup'
    end 


GO

exec testproc
5
Filip De Vos

動的SQLを引き続き使用しますが、醜いカーソルスキャフォールディングを使用しない別のアプローチ(呼び出し元にエラーをバブリングすることなく、失敗したステップと生成されたエラー番号を調べることができます):

  DECLARE @step INT = 0, @result INT = 0, @sql NVARCHAR(MAX) = N'';
  DECLARE @tbl TABLE([step] INT PRIMARY KEY, [pname] NVARCHAR(513));    
  INSERT @tbl([step],[pname]) VALUES(1,N'dbo.proc1'),(2,N'dbo.proc2'),(3,N'dbo.proc3');

  SELECT @sql += N'
    SET @step = ' + CONVERT(VARCHAR(12), 
      ROW_NUMBER() OVER (ORDER BY step)) + ';
    IF @result = 0
    BEGIN
     BEGIN TRY
      EXEC @result = ' + pname + ';
     END TRY
     BEGIN CATCH
      SET @result = ERROR_NUMBER();
      RETURN;
     END CATCH' 
  FROM @tbl ORDER BY [step] OPTION (MAXDOP 1); 

  SET @sql += REPLICATE(N' END ', @@ROWCOUNT);

  EXEC sp_executesql @sql, N'@step INT OUTPUT, @result INT OUTPUT', 
    @step = @step OUTPUT, @result = @result OUTPUT;

  PRINT 'Failed at step ' + CONVERT(VARCHAR(12), @step);
  PRINT 'Error number was ' + CONVERT(VARCHAR(12), @result);

「Failed at step」の値をステップ番号(ループ変数など)にするのか、テーブルのstepの実際の値にするのかがわかりません。これを変更して切り替えることができます。

    SET @step = ' + CONVERT(VARCHAR(12), 
      ROW_NUMBER() OVER (ORDER BY step)) + ';

これに:

    SET @step = ' + CONVERT(VARCHAR(12), step) + ';
5
Aaron Bertrand