【问题标题】:T-SQL STOP or ABORT command in SQL ServerSQL Server 中的 T-SQL STOP 或 ABORT 命令
【发布时间】:2011-01-02 22:32:55
【问题描述】:

Microsoft SQL Server T-SQL 中是否有命令告诉脚本停止处理? 我有一个脚本想保留用于存档,但我不想让任何人运行它。

【问题讨论】:

标签: sql sql-server tsql sql-scripts


【解决方案1】:

另一种解决方案是使用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 语句之前,我建议你阅读他在Flow control in T-SQL Scripts 上的教程。

【讨论】:

  • 谢谢杰德!我喜欢他的 :ON Error EXIT 示例。
  • @Pedro:如果您在工作脚本部分之间添加 GO,这将失败,因为 GOT 按批次应用。当您有 CREATE 语句等通常必须是批处理中的第一个命令时,您必须使用 GO 将脚本分成批处理。有关其他答案,请参阅 chadhoc 的 cmets
  • 杰德。感谢您提供指向“流控制”的链接,这让我大开眼界:在过去的一个小时里,我一直在玩我能想到的每一个配置,一想到我可能需要纠正多少脚本,我就哭了。 :-(
  • +1 用于优雅的 goto 和错误处理解决方案。如果你从“没有办法停下来”开始,会更清楚一点。
  • 如果脚本中有任何 GO 语句,则不起作用。另外,GOTO 从来都不是一种好的编程风格,不应该使用!
【解决方案2】:

我知道这个问题很老,并且以几种不同的方式得到了正确的回答,但没有我在类似情况下使用过的答案。 第一种方法(非常基本):

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'

您可以自己轻松地对其进行测试,以确保其行为符合预期。

【讨论】:

    【解决方案3】:

    通过使用“全局”变量,这是一种适用于 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
    

    这里是用于每个 GO-batch 的事务和 try/catch 块的相同想法。您可以尝试更改各种条件和/或让它产生错误(除以 0,参见 cmets)来测试它的行为:

    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】:

      严重性为 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;
      

      【讨论】:

      • 警告:我刚刚测试了 SET PARSEONLY ON;围绕一些“EXECUTE sp_something”调用,它不会引发任何错误,但我可以说该过程仍然被调用和处理!
      【解决方案5】:

      尽管它的描述非常明确和有力,但 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
      

      发现重复行的结果。调试 PRINT 确认 @idSess 在 IF 检查中的值大于零 - RETURN 没有中断执行!

      【讨论】:

      • 后来发现如果我指定一个返回值(如return 1),RETURN会按预期工作——退出sproc。
      【解决方案6】:

      要解决 RETURN/GO 问题,您可以将 RAISERROR ('Oi! Stop!', 20, 1) WITH LOG 放在顶部。

      这将按照RAISERROR on MSDN关闭客户端连接。

      非常的缺点是您必须是系统管理员才能使用严重性 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:你错了。客户端连接以 20 及以上的严重性关闭。所以不会有更多的批次运行。或者你能证明别的吗?
      • @gbn:不,我错了。它是 2005 年推出的 Try/Cacth。抱歉。
      • 感谢提供演示脚本,gbn!它完全按照您的指示执行 - 打印出错误消息(Oi!停止!)并停止执行!对于那些反对者——试试吧,你会喜欢的!
      • @gbn:您可能是对的,但严重级别 20 对我们来说是个问题,因为“从 19 到 25 的严重级别只能由 sysadmin 固定服务器角色的成员或具有 ALTER TRACE 权限的用户指定。 "对我们来说太糟糕了。 :)
      【解决方案7】:

      不,没有 - 您有两种选择:

      1. 将整个脚本包装在一个大的 if/end 块中,该块被简单地确保为不正确(即“如果 1=2 开始” - 这仅在脚本不包含任何 GO 语句时才有效(因为那些表示新批次)

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

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

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

      编辑:证明“return”语句是特定于批次的 - 请注意,在返回后您将继续看到结果集:

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

      【讨论】:

      • 我真的惊呆了,没有停止执行的命令。只是......哇。
      • @JacobFW - 请参阅 Erland Somerskog 的excellent summary:“每隔一段时间,我就会觉得 SQL Server 是故意设计得尽可能令人困惑。当他们计划发布新版本时他们互相问,这次我们能做些什么来迷惑用户?有时他们有点想不通,但后来有人说让我们做一些错误处理!”(Erland 一直是 SQL Server MVP 至少 20 年)
      • @JacobFW - 或 Erland 的 comment on this MS forum 关于错误处理:“微软犯有犯罪设计罪”。我想只有他能侥幸逃脱 ?(尽管我们中的许多人都同意)
      【解决方案8】:

      尝试将其作为 TSQL 脚本运行

      SELECT 1
      RETURN
      SELECT 2
      SELECT 3
      

      return 结束执行。

      RETURN (Transact-SQL)

      无条件退出查询或 程序。 RETURN 是立即的,并且 完整,可随时使用 退出程序、批次或 语句块。声明 follow RETURN 不会被执行。

      【讨论】:

      • 同样,对于包含批处理分隔符(即 GO 语句)的脚本没有帮助 - 返回是特定于批处理的。
      【解决方案9】:

      为什么不简单地将以下内容添加到脚本的开头

      PRINT 'INACTIVE SCRIPT'
      RETURN
      

      【讨论】:

      • 请注意,如果脚本包含批处理分隔符(即 GO 语句),这将不起作用 - 返回只会从第一批返回。
      • 哦!很高兴知道!也许我应该在开头放一个/*,在最后放一个*/!
      • 好点 chadHoc,我以为他指的是存储过程...谢谢
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-13
      • 2010-10-14
      • 2011-04-04
      • 1970-01-01
      • 1970-01-01
      • 2013-05-01
      • 2013-08-29
      相关资源
      最近更新 更多