SQL Server中的T-SQL STOP或ABORT命令

70

在Microsoft SQL Server T-SQL中是否有一个命令可以告诉脚本停止处理? 我有一个想要保留的脚本,但我不希望任何人运行它。

在Microsoft SQL Server T-SQL中,使用命令"RETURN"可以使脚本停止处理并返回控制权。如果您希望保留脚本,但不希望其他人能够运行它,您可以将其保存为只读文件或将其存储在受限制的文件夹中。

更多答案请参见:https://dev59.com/AXRB5IYBdhLWcg3wUV1d - Andre
这个回答解决了你的问题吗?SQL Server - 停止或中断执行 SQL 脚本 - undefined
10个回答

66

另一个解决方案是使用 GOTO 语句改变您的脚本执行流程...

DECLARE  @RunScript bit;
SET @RunScript = 0;

IF @RunScript != 1
BEGIN
RAISERROR ('Raise Error does not stop processing, so we will call GOTO to skip over the script', 1, 1);
GOTO Skipper -- This will skip over the script and go to Skipper
END

PRINT 'This is where your working script can go';
PRINT 'This is where your working script can go';
PRINT 'This is where your working script can go';
PRINT 'This is where your working script can go';

Skipper: -- Don't do nuttin!

警告!以上示例是我从Merrill Aldrich那里得到的一个例子。在您盲目实施GOTO语句之前,我建议您阅读他关于T-SQL脚本中的流程控制的教程。


1
谢谢Jed!我喜欢他的:ON Error EXIT示例。 - Phillip Senn
12
@Pedro:如果在工作脚本部分之间添加GO,那么这将失败,因为GOT适用于每个批次。当您有必须是批处理中的第一个命令的CREATE语句等时,您必须使用GO将脚本分成批次。请参见chadhoc对其他答案的评论。 - gbn
1
Jed,感谢你提供的“流程控制”链接,真是大开眼界:我花了最近一小时时间尝试各种可能的配置,感到很沮丧,因为我可能需要修改很多脚本。:( - Kevin Roche
+1 对于优雅的goto和错误处理解决方案。如果您一开始就说“没有办法停止”,可能会更清楚一些。 - Praesagus
如果脚本中有任何GO语句,则无法正常工作。此外,GOTO从来不是一种好的编程风格,不应该使用! - webMac

41
不,没有这样的东西 - 你有几个选项:
  1. 将整个脚本包含在一个大的if/end块中,并确保其不为真(即“如果1=2开始”) - 这仅适用于脚本不包含任何GO语句的情况(因为它们表示新批处理)

  2. 在顶部使用return语句(同样受到批处理分隔符的限制)

  3. 使用基于连接的方法,这将确保整个脚本(更准确地说,是整个连接)不执行 - 在脚本顶部使用类似于'SET PARSEONLY ON''SET NOEXEC ON'的语句。这将确保连接中的所有语句(或直到该设置语句被关闭)都不会执行,而只会被解析/编译。

  4. 使用注释块来注释掉整个脚本(即/*和*/)

编辑:演示'return'语句是特定于批处理的 - 请注意,在returns之后您将继续看到结果集:

select 1
return
go
select 2
return
select 3
go
select 4
return
select 5
select 6
go

2
我真的很惊讶,居然没有停止执行的命令。哇,太不可思议了。 - Jacob FW
2
@JacobFW - 请查看Erland Somerskog的精彩总结:“偶尔,我会有这样一种感觉,即SQL Server被故意设计得尽可能令人困惑。当他们计划发布新版本时,他们会互相询问:这次我们能做些什么来困惑用户呢?有时他们会有点想不出主意,但是然后有人说_让我们做一些错误处理的事情!_”(Erland至少担任了20年的SQL Server MVP) - Reversed Engineer
1
@JacobFW - 或者Erland在MS论坛上对错误处理的评论:“Microsoft有罪,犯了一个犯罪的设计。”我想只有他能逃脱(尽管我们中的许多人都同意)。 - Reversed Engineer

22

为什么不直接在脚本的开头添加以下内容呢?

PRINT 'INACTIVE SCRIPT'
RETURN

16
请注意,如果脚本包含批处理分隔符(即GO语句),则此方法将无法正常工作 - RETURN 语句只会从第一个批处理返回。 - boydc7
1
哦!那太好了!也许我应该在开头加上 /* ,在结尾加上 */! - Phillip Senn
好观点chadHoc,我以为他指的是存储过程...谢谢。 - Sparky

19
为了解决RETURN/GO问题,您可以在顶部放置RAISERROR ('Oi! Stop!', 20, 1) WITH LOG来绕过该问题。
这将根据MSDN上的RAISERROR关闭客户端连接。
但是,非常不利的一点是您必须拥有sysadmin权限才能使用severity 20。
编辑:
对于Jersey Dude的评论,这里有一个简单的演示...
RAISERROR ('Oi! Stop!', 20, 1)  WITH LOG
SELECT 'Will not run'
GO
SELECT 'Will not run'
GO
SELECT 'Will not run'
GO

再次强调,这仅适用于当前批处理。执行将在下一批处理的开头(GO之后)重新开始。 - Jersey Dude
@Jersey Dude:你错了。客户端连接关闭时,严重程度为20或以上。因此,不会再运行任何批处理。或者你能证明不是这样吗? - gbn
1
@gbn:不好意思,我错了。是在2005年引入了Try/Catch。 - neves
感谢提供演示脚本,gbn!它完全按照您指示执行 - 打印出错误消息(Oi!Stop!)并停止执行!对于那些持反对意见的人们 - 试一试,你会喜欢它的! - Nelda.techspiress
@gbn:你可能是对的,但严重级别20对我们来说是有问题的,因为“严重级别19到25只能由sysadmin固定服务器角色的成员或具有ALTER TRACE权限的用户指定。” 对我们来说太糟糕了。 :) - Jersey Dude

10

使用严重程度为20的RAISERROR将在事件查看器中报告为错误。

你可以使用SET PARSEONLY ON;(或NOEXEC)。在脚本末尾使用GO SET PARSEONLY OFF;

SET PARSEONLY ON;
-- statement between here will not run

SELECT 'THIS WILL NOT EXEC';

GO
-- statement below here will run

SET PARSEONLY OFF;

警告:我刚刚在一些“EXECUTE sp_something”调用周围测试了SET PARSEONLY ON;,它不会引发任何错误,但我可以说该过程仍然被调用和处理! - AFract

5

以下是一种有些笨拙的方法,可以与GO批次一起使用,通过使用“全局”变量来实现。

if object_id('tempdb..#vars') is not null
begin
  drop table #vars
end

create table #vars (continueScript bit)
set nocount on
  insert #vars values (1)
set nocount off

-- Start of first batch
if ((select continueScript from #vars)=1) begin

  print '1'

  -- Conditionally terminate entire script
  if (1=1) begin
    set nocount on
      update #vars set continueScript=0
    set nocount off
    return
  end

end
go

-- Start of second batch
if ((select continueScript from #vars)=1) begin

  print '2'

end
go

以下是使用事务和try/catch块实现每个GO批处理中相同想法的示例。您可以尝试更改各种条件和/或让它生成错误(除以0,请参见注释)以测试其行为:

if object_id('tempdb..#vars') is not null
begin
  drop table #vars
end

create table #vars (continueScript bit)
set nocount on
  insert #vars values (1)
set nocount off

begin transaction;
  -- Batch 1 starts here
  if ((select continueScript from #vars)=1) begin
    begin try 
      print 'batch 1 starts'

      if (1=0) begin
        print 'Script is terminating because of special condition 1.'
        set nocount on
          update #vars set continueScript=0
        set nocount off
        return
      end

      print 'batch 1 in the middle of its progress'

      if (1=0) begin
        print 'Script is terminating because of special condition 2.'
        set nocount on
          update #vars set continueScript=0
        set nocount off
        return
      end

      set nocount on
        -- use 1/0 to generate an exception here
        select 1/1 as test
      set nocount off

    end try
    begin catch
      set nocount on
        select 
          error_number() as errornumber
          ,error_severity() as errorseverity
          ,error_state() as errorstate
          ,error_procedure() as errorprocedure
          ,error_line() as errorline
          ,error_message() as errormessage;
        print 'Script is terminating because of error.'
        update #vars set continueScript=0
      set nocount off
      return
    end catch;

  end
  go

  -- Batch 2 starts here
  if ((select continueScript from #vars)=1) begin

    begin try 
      print 'batch 2 starts'

      if (1=0) begin
        print 'Script is terminating because of special condition 1.'
        set nocount on
          update #vars set continueScript=0
        set nocount off
        return
      end

      print 'batch 2 in the middle of its progress'

      if (1=0) begin
        print 'Script is terminating because of special condition 2.'
        set nocount on
          update #vars set continueScript=0
        set nocount off
        return
      end

      set nocount on
        -- use 1/0 to generate an exception here
        select 1/1 as test
      set nocount off

    end try
    begin catch
      set nocount on
        select 
          error_number() as errornumber
          ,error_severity() as errorseverity
          ,error_state() as errorstate
          ,error_procedure() as errorprocedure
          ,error_line() as errorline
          ,error_message() as errormessage;
        print 'Script is terminating because of error.'
        update #vars set continueScript=0
      set nocount off
      return
    end catch;

  end
  go

if @@trancount > 0 begin
  if ((select continueScript from #vars)=1) begin
    commit transaction
    print 'transaction committed'
  end else begin
    rollback transaction;
    print 'transaction rolled back'
  end
end

4

尝试将此作为TSQL脚本运行

SELECT 1
RETURN
SELECT 2
SELECT 3

return语句结束执行。

RETURN (Transact-SQL)

无条件地退出查询或过程。 RETURN是立即且完整的,可用于任何时候从过程、批处理或语句块中退出。跟随RETURN的语句不会被执行。


2
这对于包含批处理分隔符(即GO语句)的脚本没有帮助 - 返回值是特定于批处理的。 - boydc7

3

尽管其描述非常明确和有力,但RETURN语句在我的存储过程中无法正常工作(用于跳过进一步执行)。我不得不修改条件逻辑。这在SQL 2008和2008 R2上都会出现:

create proc dbo.prSess_Ins
(
    @sSessID    varchar( 32 )
,   @idSess     int out
)
as
begin
    set nocount on

    select  @id=    idSess
        from    tbSess
        where   sSessID = @sSessID

    if  @idSess > 0 return  -- exit sproc here

    begin   tran
        insert  tbSess  ( sSessID ) values  ( @sSessID )
        select  @idSess=    scope_identity( )
    commit
end

必须更改为:

    if  @idSess is null
    begin
        begin   tran
            insert  tbSess  ( sSessID ) values  ( @sSessID )
            select  @idSess=    scope_identity( )
        commit
    end

因为发现了重复的行而被发现。通过调试PRINTs确认了在IF检查中@idSess的值大于零 - RETURN没有终止执行!


2
后来发现,如果我指定了一个返回值(比如return 1),RETURN就像预期的那样正常退出存储过程。 - Astrogator

3

我知道这个问题很老,已经有几种正确的回答了,但是没有像我的答案一样可以在类似情况下使用的。 第一个方法(非常基础):

IF (1=0)
BEGIN
    PRINT 'it will not go there'
    -- your script here
END
PRINT 'but it will here'

第二种方法:
PRINT 'stop here'
RETURN
    -- your script here
PRINT 'it will not go there'

您可以轻松地自行测试,以确保它的行为符合预期。


1

当你想停止执行时,只需使用 SET NOEXEC ON。

 Go
 Select 'I want to kill the job after some error or based on some validation.
 Go
 Select 'Raiserror not working'
  Go
 Select 'I have to be admin to define severity 20'
 go
Select 'I got an error, come custom validation failed, I don't want to run the 
 rest  of the script'.
Go
 SET NOEXEC ON
 Select 'rest of the script should not run after NOEXEC on executed'.

这个很棒(没有引发错误等)。只需要记住,如果您需要在同一个用户会话中重新运行语句,则需要将其关闭,例如:SET NOEXEC OFF。 - Eddie Kumar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接