出错时退出并回滚脚本中的所有内容

时间:2010-05-26 08:05:09

标签: sql-server sql-server-2005 tsql transactions

我有一个TSQL脚本可以进行大量的数据库结构调整,但是当事情发生故障时让它通过是不安全的。

说清楚:

  • 使用MS SQL 2005
  • 它不是存储过程,只是脚本文件(.sql)

我所拥有的是以下顺序

BEGIN TRANSACTION
    ALTER Stuff
    GO

    CREATE New Stuff
    GO

    DROP Old Stuff
    GO
IF @@ERROR != 0
    BEGIN
  PRINT 'Errors Found ... Rolling back'
  ROLLBACK TRANSACTION
  RETURN
    END
ELSE
     PRINT 'No Errors ... Committing changes'
     COMMIT TRANSACTION

只是为了说明我正在使用的内容......不能详细说明 现在,问题......

当我引入错误(以测试事情是否被回滚)时,我得到一个声明,即ROLLBACK TRANSACTION找不到相应的BEGIN TRANSACTION。 这让我相信当真正错误并且交易已经被杀死时。 我还注意到,脚本没有完全退出错误,因此DID尝试在发生错误后执行每个语句。 (我注意到这一点,当我不期待它们出现新表时,因为它应该有回滚)

5 个答案:

答案 0 :(得分:7)

发生错误时,事务将自动回滚,并且当前批次将中止。

然而,执行继续进入下一批。因此,错误执行后批处理中的所有内容。然后,当您稍后检查错误时,您尝试回滚已经回滚的事务。

此外,要停止整个脚本,而不仅仅是当前批次,您应该使用:

raiserror('Error description here', 20, -1) with log

有关该详细信息,请参阅my answer here

所以你需要在每批后检查@error,我认为这样的事情应该有效:

BEGIN TRANSACTION
GO

ALTER Stuff
GO

if @@error != 0 raiserror('Script failed', 20, -1) with log
GO

CREATE New Stuff
GO

if @@error != 0 raiserror('Script failed', 20, -1) with log
GO

DROP Old Stuff
GO

if @@error != 0 raiserror('Script failed', 20, -1) with log
GO

PRINT 'No Errors ... Committing changes'
COMMIT TRANSACTION

答案 1 :(得分:4)

尝试使用RETURN。这将立即退出脚本或过程,不会执行以下任何语句。您可以将它与BEGIN,ROLLBACK和COMMIT TRANSACTION语句结合使用来撤消任何数据损坏:

    BEGIN
    BEGIN TRANSACTION

    <first batch>
    IF @@error <> 0
        begin
        RAISERROR ('first batch failed',16,-1)
        ROLLBACK TRANSACTION
        RETURN
        end

    <second batch>
    IF @@error <> 0
        begin
        RAISERROR ('second batch failed',16,-1)
        ROLLBACK TRANSACTION
        RETURN
        end

    PRINT 'WIN!'
    COMMIT TRANSACTION
    END

答案 2 :(得分:3)

我没有使用raiseerror解决方案,因为它失败,因为我没有管理员权限。我通过事务处理扩展了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。因此,我可以相应地提交或回滚事务。

答案 3 :(得分:0)

您可以尝试这样的事情......如果您正在使用Try块...错误级别16(或大多数应用程序错误)立即将控件传输到CATCH块而不在try中执行任何进一步的语句块...

    Begin Transaction

Begin Try

                    --  Do your Stuff

        If (@@RowCount <> 1) -- Error condition
        Begin
            Raiserror('Error Message',16,1)
        End


    Commit
End Try
Begin Catch
    IF @@Trancount > 0
    begin
        Rollback Transaction
    End

    Declare @ErrMsg varchar(4000), @Errseverity int

    SELECT @ErrMsg = ERROR_MESSAGE(),
          @ErrSeverity = ERROR_SEVERITY()

    RAISERROR(@ErrMsg, @ErrSeverity, 1)     
End Catch

希望这会有所帮助......

答案 4 :(得分:0)

SET XACT_ABORT ON
BEGIN TRAN

-- Batch 1

GO

if @@TRANCOUNT = 0 
SET NOEXEC ON;
GO

-- Batch 2

GO

if @@TRANCOUNT = 0 
SET NOEXEC ON;
GO

-- Batch 3

GO

if @@TRANCOUNT > 0 
COMMIT
GO