MSSQL Предотвратить откат при сбое триггера - PullRequest
2 голосов
/ 31 марта 2011

У меня есть триггер после вставки / обновления / удаления, который вставляет новую запись в AuditTable каждый раз, когда вставка / обновление / удаление производится в конкретную таблицу.Если вставка в AuditTable не удалась, я бы хотел, чтобы первая запись была вставлена ​​так или иначе, и ошибка регистрировалась в следующей таблице "AuditErrors".

Это то, что у меня есть, и я пробовал много разных вещей, ноЯ не могу заставить это работать, если вставка триггера в AuditTable не удалась (я проверяю это, неправильно написав имя столбца в вставке AuditTable).Примечание: @sql - это вставка в AuditTable.

DECLARE @TranCounter INT
SET @TranCounter = @@TRANCOUNT
IF @TranCounter > 0
  SAVE TRANSACTION AuditInsert;
ELSE
  BEGIN TRANSACTION;
BEGIN TRY
  EXEC (@sql)
  IF @TranCounter = 0
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
  -- roll back
  IF @TranCounter = 0
    ROLLBACK TRANSACTION;
  ELSE
    IF XACT_STATE() <> -1
      ROLLBACK TRANSACTION AuditInsert;
  -- insert error into database    
  IF @TranCounter > 0
    SAVE TRANSACTION AuditInsert;
  ELSE
    BEGIN TRANSACTION;
  BEGIN TRY
    INSERT INTO [dbo].[AuditErrors] ([AuditErrorCode], [AuditErrorMsg]) VALUES (ERROR_NUMBER(), ERROR_MESSAGE())
    IF @TranCounter = 0
      COMMIT TRANSACTION;
  END TRY
  BEGIN CATCH
    -- roll back
    IF @TranCounter = 0
      ROLLBACK TRANSACTION;
    ELSE
      IF XACT_STATE() <> -1
        ROLLBACK TRANSACTION AuditInsert;
  END CATCH
END CATCH

Ответы [ 2 ]

2 голосов
/ 07 апреля 2011

Это единственный известный мне способ отделить исходную транзакцию от действия триггера.В этом примере исходная вставка завершается, даже если вставка аудита завершается неудачно.Протестировано на 2008R2.

Это не красиво, но не откатит транзакцию!

С надежной аутентификацией все работало нормально:

create table TestTable(
    ID int identity(1,1) not null
    ,Info varchar(50) not null
    )
GO
create table AuditTable(
    AuditID int identity(1,1) not null
    ,TestTableID int not null
    ,Info varchar(10) -- The failure is the mismatch in length
)
GO

create procedure insertAudit @id int, @Info varchar(50)
as
set nocount on;
begin try
    insert into AuditTable(TestTableID,Info)
    values(@id,@Info);
end try
begin catch
    select 0
end catch;
GO

create trigger trg_TestTable on TestTable
AFTER INSERT
as
begin
set nocount on;

declare @id int,
        @info varchar(50),
        @cmd varchar(500),
        @rc int;
select @id=ID,@info=Info from inserted;

select @cmd = 'osql -S '+@@SERVERNAME+' -E -d '+DB_NAME()+' -Q "exec insertAudit @id='+cast(@id as varchar(20))+',@Info='''+@info+'''"';

    begin try
        exec @rc=sys.xp_cmdshell @cmd
        select @rc;
    end try
    begin catch
        select 0;
    end catch;
end
GO

Удалите таблицу аудита ион все еще завершает исходную транзакцию.

Cheers!

0 голосов
/ 14 августа 2014

Вместо использования sqlcmd вы можете немного поиграть с BEGIN TRAN / ROLLBACK.

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

Все, что вам нужно сделать, это повторить выполнение кода в @sql, если транзакция, в которую вставлены данные в таблицу аудита, откатывается:

TRIGGER BEGINS

<INSERT INSERTED AND DELETED TABLES INTO TABLE VARIABLES, U'LL NEED THEM>

BEGIN TRY

BEGIN TRAN

INSERT INTO AUDITTABLE SELECT * FROM @INSERTED

COMMIT

END TRY

BEGIN CATCH

ROLLBACK

REDO ORIGINAL INSERT/UPDATE/DELETE USING TRIGGER TABLE VARIABLES (@INSERTED AND @DELETED)

INSERT INTO AUDITERROS...

END CATCH

BEGIN TRAN -- THIS IS TO FOOL SQL INTO THINKING THERE'S STILL A TRANSACTION OPEN

TRIGGER ENDS
...