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
ループよりも カーソルはより良い実装のように聞こえます です。この質問に必須ではないコードの一部には、反復数の把握が含まれているため、ループ制御変数を使用しています。
後で結果の値が必要ない場合は、この方法で短くすることができます。
-- 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
動的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) + ';