SQL Server - 停止或中断SQL脚本的执行

And*_*ite 311 sql sql-server scripting exit

有没有办法立即停止在SQL Server中执行SQL脚本,如"break"或"exit"命令?

我有一个脚本在开始插入之前执行一些验证和查找,我希望它在任何验证或查找失败时停止.

Blo*_*ard 354

RAISERROR方法

raiserror('Oh no a fatal error', 20, -1) with log
Run Code Online (Sandbox Code Playgroud)

这将终止连接,从而停止脚本的其余部分运行.

请注意,严重级别为20或更高级别以及WITH LOG选项对于它以这种方式工作是必要的.

这甚至适用于GO语句,例如.

print 'hi'
go
raiserror('Oh no a fatal error', 20, -1) with log
go
print 'ho'
Run Code Online (Sandbox Code Playgroud)

会给你输出:

hi
Msg 2745, Level 16, State 2, Line 1
Process ID 51 has raised user error 50000, severity 20. SQL Server is terminating this process.
Msg 50000, Level 20, State 1, Line 1
Oh no a fatal error
Msg 0, Level 20, State 0, Line 0
A severe error occurred on the current command.  The results, if any, should be discarded.
Run Code Online (Sandbox Code Playgroud)

请注意,'ho'未打印.

注意事项:

  • 这仅在您以admin('sysadmin'角色)登录时才有效,并且还使您没有数据库连接.
  • 如果您未以管理员身份登录,则RAISEERROR()调用本身将失败,脚本将继续执行.
  • 使用sqlcmd.exe调用时,将报告退出代码2745.

参考:http://www.mydatabasesupport.com/forums/ms-sqlserver/174037-sql-server-2000-abort-whole-script.html#post761334

noexec方法

使用GO语句的另一种方法是set noexec on.这会导致跳过脚本的其余部分.它不会终止连接,但您需要noexec在执行任何命令之前再次关闭.

例:

print 'hi'
go

print 'Fatal error, script will not continue!'
set noexec on

print 'ho'
go

-- last line of the script
set noexec off -- Turn execution back on; only needed in SSMS, so as to be able 
               -- to run this script again in the same session.
Run Code Online (Sandbox Code Playgroud)

  • noexec的事情很棒.非常感谢! (17认同)
  • 棒极了!这有点像"大棒"的方法,但有时你确实需要它.请注意,它要求严重性为20(或更高)和"WITH LOG". (14认同)
  • 请注意,使用noexec方法仍然会解释脚本的其余部分,因此您仍会遇到编译时错误,例如列不存在.如果要通过跳过某些代码有条件地处理涉及缺少列的已知模式更改,我知道这样做的唯一方法是使用:r在sqlcommand模式下引用外部文件. (5认同)
  • 当我意识到时,我正在尝试这种方法并且没有得到正确的结果......在raiserror中只有一个E ... (3认同)
  • "这将终止连接" - 似乎它没有,至少那是我所看到的. (2认同)
  • @jcollum 您使用的是 20-25 级严重性吗?[文档](http://msdn.microsoft.com/en-us/library/ms178592.aspx) 说“从 20 到 25 的严重级别被认为是致命的。如果遇到致命的严重级别,客户端连接将被终止收到消息后,错误记录在错误和应用程序日志中。” (2认同)
  • 如果要在将以非sysadmin用户身份运行的脚本中使用此脚本,请确保将严重性设置为18或更低。脚本将继续执行。 (2认同)

Gor*_*ell 177

只需使用RETURN(它将在存储过程的内部和外部工作).

  • 不,它只会终止*直到下一个GO*下一批(GO之后)将照常运行 (44认同)
  • 似乎没有用go语句为我工作:( (28认同)
  • 在脚本中,您不能像在存储过程中那样使用值进行RETURN,但是您可以执行RETURN. (4认同)
  • GO 是脚本终止符或分隔符;它不是 SQL 代码。GO 只是对您正在使用的客户端的指令,用于向数据库引擎发送命令,即新脚本在 GO 分隔符之后启动。 (3认同)
  • 出于某种原因,我认为返回在脚本中不起作用,但我只是尝试过它,它确实如此!谢谢 (2认同)
  • 冒险,因为它将在下一个GO之后继续。 (2认同)
  • 这个答案对问题根本没有帮助,即“有没有办法立即停止执行 SQL 脚本”。 (2认同)

小智 48

如果你可以使用SQLCMD模式,那么咒语

:on error exit
Run Code Online (Sandbox Code Playgroud)

(包括冒号)将导致RAISERROR实际停止脚本.例如,

:on error exit

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SOMETABLE]') AND type in (N'U')) 
    RaisError ('This is not a Valid Instance Database', 15, 10)
GO

print 'Keep Working'
Run Code Online (Sandbox Code Playgroud)

将输出:

Msg 50000, Level 15, State 10, Line 3
This is not a Valid Instance Database
** An error was encountered during execution of batch. Exiting.
Run Code Online (Sandbox Code Playgroud)

批次将停止.如果未打开SQLCMD模式,您将获得有关冒号的解析错误.不幸的是,它并不是完全没有防御性的,就好像脚本是在没有处于SQLCMD模式的情况下运行一样,SQL Managment Studio正好经过了解析时间错误!不过,如果你从命令行运行它们,这很好.

  • 好评,谢谢.我将在SSMS中添加SQLCmd模式,在Query菜单下切换. (4认同)
  • 然后是咒语...但是我该如何施放魔法小姐? (2认同)

Dav*_*sky 22

我不会使用RAISERROR- SQL具有可用于此目的的IF语句.进行验证和查找并设置局部变量,然后使用IF语句中的变量值使插入成为条件.

您不需要检查每个验证测试的变量结果.您通常只使用一个标志变量来确认所有条件通过:

declare @valid bit

set @valid = 1

if -- Condition(s)
begin
  print 'Condition(s) failed.'
  set @valid = 0
end

-- Additional validation with similar structure

-- Final check that validation passed
if @valid = 1
begin
  print 'Validation succeeded.'

  -- Do work
end
Run Code Online (Sandbox Code Playgroud)

即使您的验证更复杂,您也只需要在最终检查中包含一些标志变量.

  • 我不太清楚为什么这个答案已被标记下来,因为它在技术上是正确的,而不是海报"想要"做的事情. (4认同)
  • 这比`RAISERROR`更可靠,特别是如果你不知道谁将运行脚本以及具有什么特权. (3认同)

Tz_*_*Tz_ 13

我成功地使用事务扩展了noexec开/关解决方案,以全有或全无的方式运行脚本.

set noexec off

begin transaction
go

<First batch, do something here>
go
if @@error != 0 set noexec on;

<Second batch, do something here>
go
if @@error != 0 set noexec on;

<... etc>

declare @finished bit;
set @finished = 1;

SET noexec off;

IF @finished = 1
BEGIN
    PRINT 'Committing changes'
    COMMIT TRANSACTION
END
ELSE
BEGIN
    PRINT 'Errors occured. Rolling back changes'
    ROLLBACK TRANSACTION
END
Run Code Online (Sandbox Code Playgroud)

显然编译器"理解"IF中的@finished变量,即使出现错误并且执行被禁用.但是,仅当未禁用执行时,该值才设置为1.因此,我可以相应地提交或回滚事务.


Jor*_*ker 13

在SQL 2012+,您可以使用THROW.

THROW 51000, 'Stopping execution because validation failed.', 0;
PRINT 'Still Executing'; -- This doesn't execute with THROW
Run Code Online (Sandbox Code Playgroud)

来自MSDN:

引发异常并将执行转移到TRY ... CATCH构造的CATCH块...如果TRY ... CATCH构造不可用,则会话结束.设置引发异常的行号和过程.严重性设置为16.

  • THROW 旨在替换 RAISERROR,但您无法使用它阻止同一脚本文件中的后续批次。 (4认同)
  • 如果您也想取消当前交易,请设置xact abort。 (2认同)

Jon*_*son 12

您可以将您的SQL语句包装在WHILE循环中,并在需要时使用BREAK

WHILE 1 = 1
BEGIN
   -- Do work here
   -- If you need to stop execution then use a BREAK


    BREAK; --Make sure to have this break at the end to prevent infinite loop
END
Run Code Online (Sandbox Code Playgroud)

  • 注意:这不适用于GO语句. (5认同)
  • 我有点喜欢这个看起来,它似乎比提出错误更好一点.绝对不想忘记最后的休息! (4认同)
  • 您还可以使用变量并立即将其设置在循环的顶部以避免“拆分”。`声明@ST INT; 设置@ST = 1; 当@ST = 1; 开始; 设置@ST = 0; ...; END` 更冗长,但见鬼,反正它是 TSQL ;-) (2认同)

mta*_*zva 8

这是一个存储过程吗?如果是这样,我认为你可以做一个Return,比如"Return NULL";


jar*_*ics 8

进一步的refinig Sglasses方法,上面的行强制使用SQLCMD模式,如果不使用SQLCMD模式则使用scirpt或者使用:on error exit退出任何错误
CONTEXT_INFO用于跟踪状态.

SET CONTEXT_INFO  0x1 --Just to make sure everything's ok
GO 
--treminate the script on any error. (Requires SQLCMD mode)
:on error exit 
--If not in SQLCMD mode the above line will generate an error, so the next line won't hit
SET CONTEXT_INFO 0x2
GO
--make sure to use SQLCMD mode ( :on error needs that)
IF CONTEXT_INFO()<>0x2 
BEGIN
    SELECT CONTEXT_INFO()
    SELECT 'This script must be run in SQLCMD mode! (To enable it go to (Management Studio) Query->SQLCMD mode)\nPlease abort the script!'
    RAISERROR('This script must be run in SQLCMD mode! (To enable it go to (Management Studio) Query->SQLCMD mode)\nPlease abort the script!',16,1) WITH NOWAIT 
    WAITFOR DELAY '02:00'; --wait for the user to read the message, and terminate the script manually
END
GO

----------------------------------------------------------------------------------
----THE ACTUAL SCRIPT BEGINS HERE-------------
Run Code Online (Sandbox Code Playgroud)

  • 这是我找到解决无法中止脚本的SSMS疯狂问题的唯一方法.但是我在开头添加了'SET NOEXEC OFF',如果没有在SQLCMD模式下'SET NOEXEC ON',否则实际脚本将继续运行,除非你在日志级别20引发错误. (2认同)

Cha*_*lie 8

您可以使用GOTO语句更改执行流程:

IF @ValidationResult = 0
BEGIN
    PRINT 'Validation fault.'
    GOTO EndScript
END

/* our code */

EndScript:
Run Code Online (Sandbox Code Playgroud)

  • 使用 goto 是处理异常的可接受的方法。减少变量和嵌套的数量,并且不会导致断开连接。它可能比 SQL Server 脚本允许的陈旧异常处理更可取。 (2认同)
  • 就像这里的所有其他建议一样,如果“我们的代码”包含“GO”语句,则这不起作用。 (2认同)

Joh*_*som 6

我建议你在try catch块中包装适当的代码块.然后,您可以使用严重性为11的Raiserror事件,以便在需要时中断到catch块.如果您只是希望使用raiserrors但在try块中继续执行,则使用较低的严重性.

合理?

干杯,约翰

[编辑包括BOL参考]

http://msdn.microsoft.com/en-us/library/ms175976(SQL.90).aspx

  • 它是2005年的新功能.开始尝试{sql_statement | statement_block} END TRY BEGIN CATCH {sql_statement | statement_block} END CATCH [; ] (2认同)
  • TRY-CATCH块不允许自己进入GO. (2认同)

jer*_*ung 6

我一直RETURN在这里使用,在脚本中工作或Stored Procedure

确保你ROLLBACK的事务如果你在一个,否则RETURN立即会导致一个未提交的未提交事务

  • 不适用于包含多个批次(GO 语句)的脚本 - 请参阅我的答案以了解如何执行此操作。 (6认同)

Rob*_*son 6

这些都不适用于“GO”语句。在此代码中,无论严重性是 10 还是 11,您都会得到最终的 PRINT 语句。

测试脚本:

-- =================================
PRINT 'Start Test 1 - RAISERROR'

IF 1 = 1 BEGIN
    RAISERROR('Error 1, level 11', 11, 1)
    RETURN
END

IF 1 = 1 BEGIN
    RAISERROR('Error 2, level 11', 11, 1)
    RETURN
END
GO

PRINT 'Test 1 - After GO'
GO

-- =================================
PRINT 'Start Test 2 - Try/Catch'

BEGIN TRY
    SELECT (1 / 0) AS CauseError
END TRY
BEGIN CATCH
    SELECT ERROR_MESSAGE() AS ErrorMessage
    RAISERROR('Error in TRY, level 11', 11, 1)
    RETURN
END CATCH
GO

PRINT 'Test 2 - After GO'
GO
Run Code Online (Sandbox Code Playgroud)

结果:

Start Test 1 - RAISERROR
Msg 50000, Level 11, State 1, Line 5
Error 1, level 11
Test 1 - After GO
Start Test 2 - Try/Catch
 CauseError
-----------

ErrorMessage
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Divide by zero error encountered.

Msg 50000, Level 11, State 1, Line 10
Error in TRY, level 11
Test 2 - After GO
Run Code Online (Sandbox Code Playgroud)

实现此目的的唯一方法是编写不带GO语句的脚本。有时这很容易。有时这是相当困难的。(使用类似的东西IF @error <> 0 BEGIN ...。)