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

时间:2009-03-18 17:04:21

标签: sql sql-server scripting exit

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

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

21 个答案:

答案 0 :(得分:339)

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.

答案 1 :(得分:169)

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

答案 2 :(得分:47)

如果你可以使用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正好经过了解析时间错误!不过,如果你从命令行运行它们,这很好。

答案 3 :(得分:21)

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

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

答案 4 :(得分: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

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

答案 5 :(得分: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

答案 6 :(得分:12)

在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。

答案 7 :(得分:8)

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

答案 8 :(得分: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-------------

答案 9 :(得分:6)

我建议你在try catch块中包装适当的代码块。然后,您可以使用严重性为11的Raiserror事件,以便在需要时中断到catch块。如果你只是想要反复操作但是在try块中继续执行,那么使用较低的严重性。

有意义吗?

干杯, 约翰

[编辑包含BOL参考]

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

答案 10 :(得分:6)

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

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

/* our code */

EndScript:

答案 11 :(得分:4)

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

答案 12 :(得分:4)

您可以使用RAISERROR

答案 13 :(得分:3)

这是我的解决方案:

...

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

答案 14 :(得分:3)

您可以使用GOTO语句。试试这个。这是完全适合你。

var restService = {
'request': function(args) {
    var deferred = $q.defer();
        $http({
            url: url,
            withCredentials: this.use_session,
            method: method.toUpperCase(),
            headers: headers,
            params: params,
            data: data,
            timeout: 20000
        })
        .success(angular.bind(this, function(data, status, headers, config) {
            deferred.resolve(data, status);
        }))
        .error(angular.bind(this, function(data, status, headers, config) {
    deferred.reject(data);
        }
   return deferred.promise;
}

'listAssignments': function(filters) {
  return this.request({
        'method': "GET",
        'url': "/assignments/",
        'params': filters
      });
}

}; // end restService object

 restService.listAssignments(filters)
  .then(function(data) {
    $log.debug("success " + data);  
  }, function(data) {
    $log.debug("error " + data);  
  })
  .finally(function() {
    $log.debug("In finally.... calling scrollHelper");
  });

答案 15 :(得分:2)

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

如果您在一个交易中,请确保ROLLBACK交易,否则RETURN会立即导致未提交的交易

答案 16 :(得分:1)

答案谢谢!

raiserror()工作正常,但您不应忘记return语句,否则脚本会继续运行而不会出错! (hense the raiserror不是“throwerror”;-))当然,如果有必要可以回滚!

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

答案 17 :(得分:1)

如果您只是在Management Studio中执行脚本,并希望在第一次错误时停止执行或回滚事务(如果使用),那么我认为最好的方法是使用try catch块(SQL 2005以后)。 如果您正在执行脚本文件,这在Management studio中运行良好。 存储过程也可以使用它。

答案 18 :(得分:1)

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

答案 19 :(得分:0)

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

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

答案 20 :(得分:0)

非常感谢这里的所有其他人以及我阅读的其他帖子。 但在@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'