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

是否有一种方法可以立即停止SQL服务器中SQL脚本的执行,如“break”或“exit”命令?

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

464909 次浏览

你可以使用RAISERROR

我不会使用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

即使您的验证更复杂,您也应该只需要在最终检查中包含几个标志变量。

这是存储过程吗?如果是这样,我认为你可以只做一个返回,如“返回NULL”;

将适当的代码块包装在try catch块中。然后,如果您愿意,可以使用严重程度为11的Raiserror事件,以便中断到catch块。如果你只想抛出错误,但在try块内继续执行,那么使用较低的严重程度。

TRY…抓住(transact - sql) < / >

只需使用RETURN(它可以在存储过程内部和外部工作)。

您可以将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

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

如果你在一个事务中,请确保你ROLLBACK该事务,否则RETURN将立即导致一个未提交的开放事务

这些都不能用于“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

结果:

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

唯一可行的方法是不使用GO语句编写脚本。有时候这很简单。有时候真的很困难。(使用类似IF @error <> 0 BEGIN ...的东西。)

谢谢你的回答!

raiserror()工作得很好,但你不应该忘记return语句,否则脚本继续没有错误!(因此,raiserror不是一个“throwerror”;-)),当然,如果需要,还会进行回滚!

raiserror()很好地告诉执行脚本的人出错了。

raiserror方法

raiserror('Oh no a fatal error', 20, -1) with log

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

注意,要以这种方式工作,严重级别20或更高级别和WITH LOG选项都是必要的。

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

print 'hi'
go
raiserror('Oh no a fatal error', 20, -1) with log
go
print 'ho'

将给你输出:

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.

注意没有打印'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.

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

:on error exit

(包括冒号)将导致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'

将输出:

Msg 50000, Level 15, State 10, Line 3
This is not a Valid Instance Database
** An error was encountered during execution of batch. Exiting.

批次就会停止。如果SQLCMD模式未打开,则会得到关于冒号的解析错误。不幸的是,如果脚本没有在SQLCMD模式下运行,那么它并不是完全防弹的,SQL management Studio甚至可以轻松地跳过解析时间错误!不过,如果您从命令行运行它们,这是没问题的。

进一步细化Sglasses方法,上面的行强制使用SQLCMD模式,如果不使用SQLCMD模式则终止脚本,或者在任何错误
时使用:on error exit退出 CONTEXT_INFO用于跟踪状态。< / p >
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-------------

我用一个事务成功地扩展了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

显然,编译器“理解”IF中的@finished变量,即使有一个错误并且执行被禁用。但是,只有在未禁用执行时,该值才会设置为1。因此,我可以很好地提交或回滚事务。

这就是我的解决方案:

...

BEGIN
raiserror('Invalid database', 15, 10)
rollback transaction
return
END
如果你只是在Management Studio中执行一个脚本,并且想要在第一个错误时停止执行或回滚事务(如果使用),那么我认为最好的方法是使用try catch block (SQL 2005以后)。 如果您正在执行脚本文件,这在Management studio中工作得很好。 Stored proc也可以使用这个

你可以使用GOTO语句。试试这个。这对你很有用。

WHILE(@N <= @Count)
BEGIN
GOTO FinalStateMent;
END


FinalStatement:
Select @CoumnName from TableName

在SQL 2012+中,可以使用

THROW 51000, 'Stopping execution because validation failed.', 0;
PRINT 'Still Executing'; -- This doesn't execute with THROW

从MSDN:

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

你可以使用转到语句改变执行流:

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


/* our code */


EndScript:

在过去,我们使用了以下语句:效果最好:

RAISERROR ('Error! Connection dead', 20, 127) WITH LOG

将它包含在try catch块中,然后执行将被转移到catch。

BEGIN TRY
PRINT 'This will be printed'
RAISERROR ('Custom Exception', 16, 1);
PRINT 'This will not be printed'
END TRY
BEGIN CATCH
PRINT 'This will be printed 2nd'
END CATCH;
非常感谢这里的其他人以及我读过的其他帖子。 但是没有什么能满足我所有的需求,直到@jaraics回答

我所见过的大多数答案都忽略了具有多个批处理的脚本。他们忽略了SSMS和SQLCMD的双重用法。我的脚本在SSMS中是完全可运行的——但我想要F5预防,这样他们就不会在意外中删除现有的对象集。

SET PARSEONLY ON工作得很好,足以防止不必要的F5。但是你不能用SQLCMD运行。

另一件让我慢了一段时间的事情是Batch在出现错误时如何跳过任何进一步的命令——所以我的SET NOCOUNT ON被跳过,因此脚本仍然运行。

不管怎样,我稍微修改了一下jaraics的答案:(在这种情况下,我还需要一个数据库从命令行激活)

-----------------------------------------------------------------------
-- Prevent accidental F5
-- Options:
--     1) Highlight everything below here to run
--     2) Disable this safety guard
--     3) or use SQLCMD
-----------------------------------------------------------------------
set NOEXEC OFF                             -- Reset in case it got stuck ON
set CONTEXT_INFO  0x1                      -- A 'variable' that can pass batch boundaries
GO                                         -- important !
if $(SQLCMDDBNAME) is not null
set CONTEXT_INFO 0x2                   -- If above line worked, we're in SQLCMD mode
GO                                         -- important !
if CONTEXT_INFO()<>0x2
begin
select 'F5 Pressed accidentally.'
SET NOEXEC ON                          -- skip rest of script
END
GO                                         -- important !
-----------------------------------------------------------------------


< rest of script . . . . . >




GO
SET NOEXEC OFF
print 'DONE'

我一直在使用以下脚本,你可以在我的答案是中看到更多细节。

RAISERROR ( 'Wrong Server!!!',18,1) WITH NOWAIT RETURN
    

print 'here'
    

select [was this executed]='Yes'