Выйти и откатить все в скрипте по ошибке - PullRequest
8 голосов
/ 26 мая 2010

У меня есть сценарий 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. Это наводит меня на мысль, что что-то, когда ДЕЙСТВИТЕЛЬНО неправильно и транзакция уже была убита. Я также заметил, что сценарий не полностью завершился при ошибке и, таким образом, пытался выполнить каждое утверждение после возникновения ошибки. (Я заметил это, когда появились новые таблицы, когда я их не ожидал, потому что они должны были откатиться)

Ответы [ 5 ]

7 голосов
/ 26 мая 2010

При возникновении ошибки транзакция автоматически откатывается и текущий пакет прерывается.

Однако выполнение продолжается в следующем пакете. Таким образом, все вещи в пакетах после ошибки выполняются. А затем, когда вы позже проверяете ошибки, вы пытаетесь откатить уже откат транзакции.

Кроме того, чтобы остановить весь скрипт, а не только текущий пакет, вы должны использовать:

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

См. мой ответ здесь для подробностей об этом.

Так что вам нужно проверять @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
4 голосов
/ 13 июня 2012

Попробуйте использовать 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
3 голосов
/ 25 октября 2011

Я не использовал решение RaiseRerror, потому что оно не получилось, так как у меня не было прав администратора. Я расширил решение noexec on / off с помощью обработки транзакций следующим образом:

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

Видимо, компилятор "понимает" переменную @finished в IF, даже если произошла ошибка и выполнение было отключено. Однако значение устанавливается равным 1, только если выполнение не было отключено. Следовательно, я могу соответственно зафиксировать или откатить транзакцию.

0 голосов
/ 09 апреля 2013
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
0 голосов
/ 26 мая 2010

Вы можете попробовать что-то вроде этого ... Если вы используете блок Try ... Уровень ошибки 16 (или большая часть ошибок приложения) немедленно передает управление в блок CATCH без выполнения каких-либо дальнейших операторов в попытке блок ...

    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

Надеюсь, это поможет ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...