按顺序运行多个存储过程

5 sql-server stored-procedures sql-server-2008-r2

我有一个用 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
Run Code Online (Sandbox Code Playgroud)

当我运行该过程时,SQL Server 给我一个错误,因为作为@result动态 SQL 一部分的变量没有定义为包含在该@sql变量中的批处理的一部分。如果我像这样修改它:

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

我收到语法错误。

除了检索子过程的返回值之外,这工作正常。有没有办法实现我既定的目标,如果有,如何实现?

注意:根据我在这里提出的问题,游标听起来WHILE循环更好,尤其是考虑到表变量。对这个问题不重要的部分代码涉及知道迭代次数,因此使用循环控制变量。

Fil*_*Vos 5

如果您以后不需要结果值,您可以通过这种方式缩短它:

-- 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
Run Code Online (Sandbox Code Playgroud)

变量引擎将生成以下查询批处理

exec @result = [proc1] if @result <> 0 return;
exec @result = [proc2] if @result <> 0 return;
exec @result = [proc3] if @result <> 0 return;
Run Code Online (Sandbox Code Playgroud)

执行批处理时,当 @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
Run Code Online (Sandbox Code Playgroud)


Aar*_*and 5

另一种方法,它仍然使用动态 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);
Run Code Online (Sandbox Code Playgroud)

不确定您是否希望“步骤失败”值是步骤编号(如您的循环变量)或step表中的实际值。你可以通过改变这个来切换:

    SET @step = ' + CONVERT(VARCHAR(12), 
      ROW_NUMBER() OVER (ORDER BY step)) + ';
Run Code Online (Sandbox Code Playgroud)

对此:

    SET @step = ' + CONVERT(VARCHAR(12), step) + ';
Run Code Online (Sandbox Code Playgroud)