2011-03-31 110 views
2

我有一個插入/更新後/刪除觸發器,其中每個插入/更新/刪除是一個特定的表進行一次插入一條新記錄在AuditTable。如果AuditTable插入失敗,我想第一個記錄被插入反正並記錄在進一步表「AuditErrors」的錯誤。MSSQL防止回滾時觸發失敗

這就是我到目前爲止,我嘗試了很多不同的東西,但如果觸發器插入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

這是我知道將原始事務與觸發操作分開的唯一方法。在此示例中,即使審計插入失敗,原始插入也會完成。經過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 '[email protected]@SERVERNAME+' -E -d '+DB_NAME()+' -Q "exec insertAudit @id='+cast(@id as varchar(20))+',@Info='''[email protected]+'''"'; 

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

刪除審覈表,它仍然完成原來的事務。

乾杯!

+0

乾杯隊友,我沒有試過這但是,但我會在某個階段,讓你知道! – Martin 2011-04-12 14:17:29

0

而不是使用SQLCMD的,你可以考慮用BEGIN TRAN /回滾一點點打。

需要注意的是,即使壽回滾命令將撤消,因爲這引起了觸發器引發的語句開始的每次改變,任何改變由後續命令不會做。

所有你需要做的就是重複代碼的執行中@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