views:

311

answers:

2

I have some code in an after insert trigger that may potentially fail. Such a failure isn't crucial and should not rollback the transaction. How can I trap the error inside the trigger and have the rest of the transaction execute normally?

The example below shows what I mean. The trigger intentionally creates an error condition with the result that the original insert ( "1" ) never inserts into the table. Try/Catch didn't seem to do the trick. A similar, older stack overflow question didn't yield an answer except for "prevent the error from occuring in the first place" - which isn't always possible/easy.

Any other ideas?

create table test 
(
  a int not null
);
go

create trigger testTrigger on test 
after insert as 
begin 
  insert into test select null;
end;
go

insert into test values ( 1 );
+1  A: 

A trigger cannot fail and still have the transaction roll forward. You have a few options to ensure that the trigger does not fail.

1 - You can ensure that the after does not fail by duplicating the logic for checking the constraints and not attempting an operation which would violate the constraints:

i.e.

INSERT INTO test WHERE val IS NOT NULL

2 - You can defer the potentially failing action by using a queue design pattern where actions which may or may not fail are queued by enqueueing to a table where the enqueueing operation cannot possibly fail.

i.e.

INSERT INTO ACTION_QUEUE (action, parameters) VALUES ('INSERT INTO TEST', val)
Cade Roux
I'm afraid that this was gonna be the only way. It's difficult though. Please see my comment to Quassnoi's reply below.
BuschnicK
@BuschnicK Making triggers is one of the more invasive things you can do in a system. It is part of the database schema for enforcing database behavior and you are doing this in someone else's database. They have their place, but I don't think this usage is appropriate IF you can't ensure that they behave well. Frankly if you are using a trigger as your only mechanism to ship changes, if the trigger was to fail silently, your changes wouldn't get shipped, yet the transaction could complete and you would lose the change data "message", how would you re-sync your data reliably anyway?
Cade Roux
@BuschnicK You would be better off looking into some kind of change data capture or other ETL technique to compare changes since last snapshot.
Cade Roux
+1  A: 

Due to the way triggers are implemented in SQL Server, all constraint violations within the triggers doom the transactions.

This is the same as doing:

DROP TABLE test

CREATE TABLE test 
(
        a INT NOT NULL
)

GO

SET XACT_ABORT ON
GO

BEGIN TRANSACTION

BEGIN TRY
        INSERT
        INTO    test
        SELECT  NULL
END TRY
BEGIN CATCH
        INSERT
        INTO    test
        SELECT  1
END CATCH

which results in a doomed transaction, except that there is no way to disable XACT_ABORT inside a trigger.

SQL Server also lacks autonomous transactions.

That's another reason why you should put all you logic into the stored procedures rather than triggers.

Quassnoi
actually you do not have doomed transactions if you do not use try..catch.
AlexKuznetsov
@Alex: sure, the operation is just atomically cancelled. This was just to demonstrate what's going on.
Quassnoi
How do stored procedures help? I thought if anything the trigger would call the stored procedure... To give a bit of background info of what I'm trying to archieve here: We have a vendor supplied ERP system and want to get live up to date data from it's tables. We attach triggers to their tables, massage the data and copy it over to our own database. We want to guarantuee our triggers will never fail because that would cause problems in the original ERP system.
BuschnicK
@Buschnick: I understand, and this would be the best solution in theory. However, the triggers are implemented the way the are, and that means that a failure in a trigger will lead to a failure in the operation that fired it. Usually, this is worked around by making a stored procedure a single entry point to the tables. This, however, will be impossible with a `ERP` system that is not under your control. The best you can do is query the `ERP` tables directly from you database, use a replication (which will introduce a little lag) or use a combination of both to minimize the impact and the lag.
Quassnoi