MSSQL防止触发器失败时回滚

发布于 2024-10-28 07:49:01 字数 1151 浏览 0 评论 0原文

我有一个插入/更新/删除后触发器,每次对特定表进行插入/更新/删除时,它都会在 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

I have an after insert/update/delete trigger, which inserts a new record in an AuditTable every time an insert/update/delete is made to a specific table. If the insertion in the AuditTable fails I'd like the first record to be inserted anyway and the error logged in a further table "AuditErrors".

This is what I have so far and I tried many different things but I can't get this to work if the trigger insert into the AuditTable fails (I test this by misspelling the name of a column in the AuditTable insert). NB: @sql is the insert into the 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

红墙和绿瓦 2024-11-04 07:49:01

这是我所知道的将原始交易与触发操作分开的唯一方法。在此示例中,即使审核插入失败,原始插入也会完成。在 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

删除审计表,它仍然完成原始事务。

干杯!

This is the only way I know of separating the original transaction from the trigger action. In this example the original insert completes even though the audit insert fails. Tested on 2008R2.

It's not pretty but it won't rollback the transaction!

It worked just fine with trusted authentication:

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

Drop the Audit table and it still completes the original transaction.

Cheers!

开始看清了 2024-11-04 07:49:01

您可以考虑使用 BEGIN TRAN/ROLLBACK 来代替使用 sqlcmd。

请注意,即使回滚命令将撤消自导致触发器触发的语句开始以来所做的所有更改,后续命令所做的任何更改都不会。

如果审计表中插入数据的事务回滚,您所要做的就是重复执行@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

Instead of using sqlcmd, you may consider playing with BEGIN TRAN/ROLLBACK a little bit.

Note that, even tho a rollback command will undo every change made since the start of the statement which caused the trigger to fire, any changes made by subsequent commands will not.

All you have to do is to repeat the execution of the code in @sql if the transaction in which data is inserted in the audit table gets rolled back:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文