【发布时间】:2010-10-14 03:17:47
【问题描述】:
有没有办法立即停止 SQL 服务器中 SQL 脚本的执行,例如“break”或“exit”命令?
我有一个脚本在开始插入之前会进行一些验证和查找,如果任何验证或查找失败,我希望它停止。
【问题讨论】:
标签: sql sql-server scripting exit
有没有办法立即停止 SQL 服务器中 SQL 脚本的执行,例如“break”或“exit”命令?
我有一个脚本在开始插入之前会进行一些验证和查找,如果任何验证或查找失败,我希望它停止。
【问题讨论】:
标签: sql sql-server scripting exit
非常感谢这里的所有其他人和我读过的其他帖子。 但在@jaraics 回答之前,没有什么能满足我的所有需求。
我见过的大多数答案都忽略了多批次的脚本。他们忽略了 SSMS 和 SQLCMD 中的双重用法。我的脚本在 SSMS 中完全可以运行——但我希望 F5 预防,这样他们就不会意外删除现有的一组对象。
SET PARSEONLY ON 工作得很好,可以防止不需要的 F5。但是你不能使用 SQLCMD 运行。
另一件事让我慢了一阵子,当出现错误时,批处理将如何跳过任何进一步的命令 - 所以我的 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'
【讨论】:
将其包含在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;
【讨论】:
过去我们使用以下...效果最好:
RAISERROR ('Error! Connection dead', 20, 127) WITH LOG
【讨论】:
我一直在这里使用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 方法
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' 没有打印出来。
注意事项:
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.
【讨论】:
您可以使用GOTO 语句更改执行流程:
IF @ValidationResult = 0
BEGIN
PRINT 'Validation fault.'
GOTO EndScript
END
/* our code */
EndScript:
【讨论】:
在 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。
【讨论】:
您可以使用 GOTO 语句。尝试这个。这对你来说已经用完了。
WHILE(@N <= @Count)
BEGIN
GOTO FinalStateMent;
END
FinalStatement:
Select @CoumnName from TableName
【讨论】:
如果您只是在 Management Studio 中执行脚本,并且希望在第一个错误时停止执行或回滚事务(如果使用),那么我认为最好的方法是使用 try catch 块(SQL 2005 及更高版本)。 如果您正在执行脚本文件,这在 Management Studio 中效果很好。 存储过程也可以一直使用它。
【讨论】:
这是我的解决方案:
...
BEGIN
raiserror('Invalid database', 15, 10)
rollback transaction
return
END
【讨论】:
我不会使用 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
即使您的验证更复杂,您也应该只需要在最终检查中包含几个标志变量。
【讨论】:
RAISERROR 可靠得多,尤其是在您不知道谁将运行脚本以及拥有什么权限的情况下。
declare @i int = 0; if @i=0 begin select '1st stmt in IF block' go end else begin select 'ELSE here' end go
我使用事务成功扩展了 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。因此,我可以很好地提交或回滚相应的事务。
【讨论】:
IF (XACT_STATE()) <> 1 BEGIN Set NOCOUNT OFF ;THROW 525600, 'Rolling back transaction.', 1 ROLLBACK TRANSACTION; set noexec on END; 但执行从未停止,我最终引发了三个“回滚事务”错误。有什么想法吗?
进一步完善 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-------------
【讨论】:
SET PARSEONLY ON 工作得很好。但是你不能用 SQLCMD 运行。我也没有看到关于 SET NOCOUNT ON 当同一批中的任何东西都没有编译时不起作用的评论——这让我在一段时间内横盘整理。我在下面的答案中对此添加了一点点。
如果你可以使用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 Managment Studio 甚至可以轻松克服解析时间错误!不过,如果您从命令行运行它们,这很好。
【讨论】:
谢谢你的回答!
raiserror() 工作正常,但您不应忘记return 语句,否则脚本将继续运行而不会出错! (因此 raiserror 不是“throwerror” ;-))当然如果有必要会进行回滚!
raiserror() 很高兴告诉执行脚本的人出了点问题。
【讨论】:
我建议您将适当的代码块包装在 try catch 块中。然后,您可以使用严重性为 11 的 Raiserror 事件,以便根据需要中断到 catch 块。如果您只想引发错误但在 try 块内继续执行,请使用较低的严重性。
有意义吗?
干杯, 约翰
[已编辑以包含 BOL 参考]
http://msdn.microsoft.com/en-us/library/ms175976(SQL.90).aspx
【讨论】:
您可以将 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
【讨论】:
DECLARE @ST INT; SET @ST = 1; WHILE @ST = 1; BEGIN; SET @ST = 0; ...; END 更冗长,但见鬼,反正它是 TSQL ;-)
只需使用 RETURN(它可以在存储过程的内部和外部工作)。
【讨论】:
这是一个存储过程吗?如果是这样,我认为您可以只做一个返回,例如“返回NULL”;
【讨论】:
你可以使用RAISERROR。
【讨论】: