有没有一种方法可以立即停止在SQL Server中执行SQL脚本,例如“ break”或“ exit”命令?

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

#1 楼

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'角色)身份登录时有效,并且也没有数据库连接。
如果您未以admin身份登录,则RAISEERROR()调用本身将失败并且脚本将继续执行。
使用sqlcmd.exe调用时,将报告退出代码2745。

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

noexec方法

与GO语句一起使用的另一种方法s 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.


评论


棒极了!这有点“大棒”的方法,但是有时候您确实需要它。请注意,它要求严重性为20(或更高)和“ WITH LOG”。

–洛克驻军
09年9月17日在15:41

请注意,使用noexec方法时,脚本的其余部分仍会被解释,因此您仍然会遇到编译时错误,例如不存在column。如果要跳过某些代码来有条件地处理涉及缺少列的已知模式更改,我知道的唯一方法是在sqlcommand模式下使用:r引用外部文件。

– David Eison
2012年3月31日15:48

noexec很棒。非常感谢!

– Gaspa79
13年4月8日在20:03

我尝试这种方法,但没有意识到结果不正确...提高跟踪器中只有一个E ...

–bobkingof12vs
16年1月29日在15:13

如果要在将以非sysadmin用户身份运行的脚本中使用此脚本,请确保将严重性设置为18或更低。脚本将继续执行。

–爱丹
16-10-18在23:20

#2 楼

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

评论


由于某种原因,我当时认为return在脚本中不起作用,但是我只是尝试了,它确实起作用了!谢谢

–安迪·怀特(Andy White)
09年3月18日在18:17

在脚本中,不能像在存储过程中那样使用值执行RETURN操作,但是可以执行RETURN操作。

–洛克驻军
09年9月17日在16:08

不,它只会终止直到下一个GO。下一批(GO之后)将照常运行

–抵押
13年5月5日在15:46

冒充危险,因为它将在下一个GO之后继续。

–贾斯汀
16年6月2日在4:21

GO是脚本终止符或定界符;这不是SQL代码。 GO只是对客户端的一条指令,该客户端用于将命令发送到数据库引擎,该命令表明在GO分隔符之后将启动一个新脚本。

–反向工程师
17-10-24在8:38

#3 楼

如果可以使用SQLCMD模式,则该咒语(包括冒号)将导致RAISERROR实际上停止脚本。例如,

:on error exit


将输出:

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


,该批次将停止。如果未打开SQLCMD模式,则将出现有关冒号的解析错误。不幸的是,它不是完全防弹的,就好像该脚本在未处于SQLCMD模式下运行时一样,SQL Managment Studio甚至在解析时间错误时都轻而易举!不过,如果您是从命令行运行它们,那很好。

评论


很好的评论,谢谢。我将添加在SSMS SQLCmd模式下在“查询”菜单下切换。

–大卫·彼得斯(David Peters)
2012-12-19 17:31

这很有用-表示您在运行时不需要-b选项

–JonnyRaa
2015年1月8日17:16

然后是咒语...但是我该如何施放魔法导弹?

– JJS
17年2月3日在18:31

完善。不需要sysadmin ultra额外的用户权限

– Pac0
18-11-5在10:40



#4 楼

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


即使您的验证比较复杂,也只需要几个标志变量即可包括在您的最终支票中。

评论


是的,我在脚本的其他部分中使用了IF,但是我不想在尝试执行插入操作之前检查每个局部变量。我宁愿停止整个脚本,并强迫用户检查输入。 (这只是一个快速而肮脏的脚本)

–安迪·怀特(Andy White)
09年3月18日在17:14

我不太确定为什么这个答案会被标记下来,因为它在技术上是正确的,而不是发帖人“想要”做什么。

–约翰·桑索姆(John Sansom)
09年3月18日在17:17

是否可以在Begin..End中包含多个块?含义声明;走;声明;走;等等等等?我遇到错误,我想这可能是原因。

–乔尔·佩尔顿(Joel Peltonen)
2014年5月6日12:48

这比RAISERROR可靠得多,尤其是在您不知道谁将运行脚本以及具有哪些特权的情况下。

–密码
2015年10月15日16:10

@John Sansom:我在这里看到的唯一问题是,如果您尝试分支到GO语句,则IF语句不起作用。如果您的脚本依赖于GO语句(例如DDL语句),这将是一个大问题。这是没有第一个go语句的示例:声明@i int = 0;如果@ i = 0开始选择'IF块中的第1个stmt'结束,否则开始选择'此处的ELSE'结束

–詹姆斯·詹森(James Jensen)
18-2-21在13:55



#5 楼

在SQL 2012+中,可以使用THROW。

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


从MSDN:


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


评论


THROW旨在替换RAISERROR,但您不​​能阻止它与它一起在同一脚本文件中进行后续批处理。

– NReilingh
16-11-25在21:00

正确@NReilingh。那是Blorgbeard的答案实际上是唯一的解决方案。但是,它确实需要sysadmin(严重性级别20),并且如果脚本中没有多个批处理,则它的处理非常繁琐。

–乔丹·帕克
16-11-27在17:27



如果您也想取消当前交易,请设置xact abort。

– Nurettin
18年1月16日在13:42

#6 楼

我通过事务成功扩展了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。因此,我可以很好地相应地提交或回滚事务。

评论


我不明白。我按照指示进行。我在每个GO之后输入了以下SQL。 IF(XACT_STATE())<> 1 BEGIN设置NOCOUNT OFF;抛出525600,'回滚事务',1 ROLLBACK TRANSACTION;在END上设置noexec;但是执行从未停止,最终我遇到了三个“回滚事务”错误。有任何想法吗?

–user1161391
19年7月19日在22:07

#7 楼

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


评论


我有点喜欢这种外观,似乎比引发错误要好一些。绝对不想忘记最后的休息!

–安迪·怀特(Andy White)
09年3月18日在18:10

您还可以使用变量,然后将其立即设置在循环的顶部,以避免“分裂”。声明@ST INT; SET @ST = 1; @ST = 1时开始; SET @ST = 0; ...;结束语比较冗长,但不管怎么说,它是TSQL ;-)

–user166390
2012年10月10日,0:15



这是某些人执行goto的方式,但是跟goto相比,它更令人困惑。

– Nurettin
18年1月16日在13:44

这种方法可以防止意外的GO。感激。

–it3xl
18年1月18日在14:36

#8 楼

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

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

/* our code */

EndScript:


评论


使用goto是处理异常的可接受方法。减少变量和嵌套的数量,并且不会导致断开连接。它可能比SQL Server脚本允许的过时异常处理更好。

– Antonio Drusin
19年11月20日在14:48

就像这里的所有其他建议一样,如果“我们的代码”包含“ GO”语句,则此方法将无效。

–迈克·格莱德希尔(Mike Gledhill)
20年10月10日在12:08

奇怪的是,这仍然解析了脚本。我收到“关键字'with'附近的语法不正确”错误,但是我能够阻止脚本在没有sysadmin的情况下运行(删除所有“ GO”命令后)。谢谢。

–有效
20-09-15在19:30

#9 楼

进一步使用refinig Sglasses方法时,以上几行强制使用SQLCMD模式,如果不使用SQLCMD模式,则终止脚本,或在出现任何错误时使用: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-------------


评论


这是我发现无法中止脚本的SSMS疯狂方法的唯一方法。但是我在开头添加了“ SET NOEXEC OFF”,如果不在SQLCMD模式下则添加了“ SET NOEXEC ON”,否则实际脚本将继续运行,除非您在日志级别20处引发错误。

– Mark Sowul
2012年2月10日19:49



#10 楼

这是存储过程吗?如果是这样,我想您可以做一个Return,例如“ Return NULL”;

评论


谢谢您的回答,很高兴知道,但是在这种情况下,它不是存储的proc,只是一个脚本文件

–安迪·怀特(Andy White)
09年3月18日在17:20

@戈登并非总是(我在这里搜索)。查看其他答案(GO将其绊倒,是一回事)

– Mark Sowul
2014年1月27日14:21



#11 楼

我建议您将适当的代码块包装在try catch块中。然后,您可以使用严重性为11的Raiserror事件,以便根据需要中断到catch块。如果您只想提高筹码,但继续在try块中执行,则使用较低的严重性。

有道理吗?

干杯,
John

[已编辑,包括BOL参考]

http://msdn.microsoft.com/zh-cn/library/ms175976(SQL.90).aspx

评论


我从没看过SQL的try-catch-您介意发布一个简短的示例说明您的意思吗?

–安迪·怀特(Andy White)
09年3月18日在17:20

它是2005年的新功能。开始尝试{sql_statement | statement_block}结束尝试开始{sql_statement | statement_block}结束捕获[; ]

–山姆
09年3月18日在20:22

@Andy:添加了参考,包括示例。

–约翰·桑索姆(John Sansom)
09年3月19日在12:30

TRY-CATCH块不允许itelf进入GO。

– AntonK
13年8月9日在13:26

#12 楼

我一直在这里使用RETURN,可以在脚本中使用,也可以在Stored Procedure中使用。

如果您在同一笔交易中,请确保ROLLBACK,否则RETURN将立即导致未提交的未清交易

评论


不适用于包含多个批次的脚本(GO语句)-有关如何执行此操作的信息,请参阅我的答案。

–蓝胡子出局了
09年4月29日在23:45

RETURN只是退出当前语句块。如果您在IF END块中,则执行将在END之后继续。这意味着您不能在测试某些条件后使用RETURN结束执行,因为您将始终处于IF END块中。

– cdonner
2012年5月15日14:58

#13 楼

您可以使用RAISERROR。

评论


如果提出可以在插入之前进行验证的方法,那么提出一个可以避免的错误(假设我们在这里指的是参照验证)是没有道理的。

–戴夫·斯沃斯基(Dave Swersky)
09年3月18日在17:14

raiserror可以用作严重性设置较低的参考消息。

–摩登·普拉迪奇(Mladen Prajdic)
09年3月18日在17:42

除非满足接受的答案中所述的某些条件,否则脚本将继续。

– Eric J.
13年5月5日在22:11

#14 楼

这些都不适用于'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 ...的东西。)

评论


使用CREATE PROCEDURE等无法做到这一点。有关解决方案,请参见我的回答。

–蓝胡子出局了
09年4月29日在23:44

Blogbeard的解决方案很棒。我已经使用SQL Server多年了,这是我第一次看到它。

–洛克驻军
09年9月17日在16:07

#15 楼

这是我的解决方案:

...

BEGIN
    raiserror('Invalid database', 15, 10)
    rollback transaction
    return
END


#16 楼

您可以使用GOTO语句。尝试这个。这对您已经用光了。

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

FinalStatement:
     Select @CoumnName from TableName


评论


GOTO被认为是一种不良的编码习惯,建议使用“ TRY..CATCH”,因为它是从SQL Server 2008开始引入的,随后是2012年的THROW。

–艾迪·库玛(Eddie Kumar)
18年11月15日在16:08

#17 楼

谢谢!!

raiserror()可以正常工作,但您不应忘记return语句,否则脚本将继续运行而不会出错! (因此,引发举报不是“ throwerror” ;-)),当然,如果需要的话,还可以进行回滚!

raiserror()很高兴告诉执行脚本的人出了点问题。

#18 楼

如果您只是在Management Studio中执行脚本,并且想在出现第一个错误时停止执行或回滚事务(如果使用),那么我认为最好的方法是使用try catch块(SQL 2005及更高版本)。
此如果您正在执行脚本文件,则在Management Studio中效果很好。
存储的proc也可以始终使用它。

评论


您的答案加上60多个投票后会增加什么?你看了吗?检查此metaSO问题和有关如何给出正确答案的Jon Skeet:编码博客。

–雅罗斯拉夫
2012年10月11日13:38



#19 楼

将其放在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;


#20 楼

过去,我们使用了以下方法……效果最佳:

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