Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
Ogreite
Starting Member
5 Posts |
Posted - 2006-04-11 : 05:53:12
|
Hi all, I have a question about recovering from errors in a trigger, I hope you can help me.I have a situation where I need to record an audit for every modification (delete, update and insert) on certain tables. A stored procedure called from triggers seem to be ideal, but the catch is I dont want to interfere whatsoever with the original data modifications.The problem arises when an error occurs in my trigger. If the trigger crashes, I loose the original data modifications! I dont want that to happen...if the trigger (or the SP that it calls) crashes, then too bad. I can live with loosing the audit trail, but I dont want it to affect anything else.I havent been able to successfully try and catch the errors without the original data changes (that starts the trigger) getting lost or rolled back. I use the AFTER trigger type, and apparantly this is only fired after the triggering SQL statement is executed successfully. So basically at this point I want this stuff to get commited before I attempt auditting it, if possible, but I have read on the net that all errors terminate a trigger batch and roll back the transaction on the spot. I believe this will be tricky to do because I have also read that "each INSERT, UPDATE and DELETE statement is its own transaction in SQL Server, and the trigger is part of that transaction".This is my basic layout of my tigger and the SP that it calls:CREATE TRIGGER [TestTrigger] ON [dbo].[someTable]AFTER insert, update, delete AS BEGIN SET NOCOUNT ON; BEGIN TRY EXEC [dbo].[TestProcedure] 'someValue' END TRY BEGIN CATCH raiserror('Error in Trigger!', 16, -1) END CATCH; END;CREATE PROCEDURE [dbo].[TestProcedure] ( @someValue varchar(50) )ASBEGIN BEGIN TRY END TRY BEGIN CATCH raiserror('Error in TestProcedure!', 16, -1) END CATCH;END;Does anyone have any ideas how I can solve this? |
|
|
rmason
Starting Member
12 Posts |
Posted - 2006-04-11 : 08:33:25
|
| If I've got this right, an After trigger will execute the trigger code AFTER the insert/update/delete on the parent table has occured but you are still within the same transaction. Therefore if your trigger code fails for some reason then the WHOLE transaction has to roll back including the insert/update/delete. The only way you could avoid this is if you were to submit the statement without it being in a transaction, that way the insert/update/delete should remain even if there is a subsequent error.Personally I would keep the whole thing ina transaction. The likelyhood of an error occuring while writing to an audit table has to be very slight and in my opinion if that was to error then it show's there's something majorly wrong with my app and I'd want the whole thing to rollback.Rob. |
 |
|
|
Ogreite
Starting Member
5 Posts |
Posted - 2006-04-11 : 08:54:40
|
Thanks for the reply, Rob.quote: Originally posted by rmason If I've got this right, an After trigger will execute the trigger code AFTER the insert/update/delete on the parent table has occured but you are still within the same transaction. Therefore if your trigger code fails for some reason then the WHOLE transaction has to roll back including the insert/update/delete. The only way you could avoid this is if you were to submit the statement without it being in a transaction, that way the insert/update/delete should remain even if there is a subsequent error.
What do you mean by submitting the statement without it being in a transaction? Which statement?quote: Personally I would keep the whole thing ina transaction. The likelyhood of an error occuring while writing to an audit table has to be very slight and in my opinion if that was to error then it show's there's something majorly wrong with my app and I'd want the whole thing to rollback.Rob.
Well, indeed I hope that the likelyhood of an error occuring while writing to my audit will be slight. But you cant predict everything...they invented try - catch blocks for a reason. My problem is that even by catching most errors, the whole transaction still gets either rolled back or aborted and I loose the data changes made by the user. Perhaps there is no way around it, which is a shame realy because triggers make things like auditing very quick and easy and low maintenance. |
 |
|
|
blindman
Master Smack Fu Yak Hacker
2365 Posts |
Posted - 2006-04-11 : 13:01:49
|
| So why is your trigger crashing? Triggers should be simple code, and thus relatively easy to debug. I have to say, I dislike the idea of calling a sproc from a trigger. A scalar function, maybe, but not a sproc. You end up with spaghetti code. All the logic for a trigger should be encapsulated within the trigger, making the table somewhat like an OOP object which is sufficient and complete unto itself. |
 |
|
|
Ogreite
Starting Member
5 Posts |
Posted - 2006-04-11 : 19:16:15
|
quote: Originally posted by blindman So why is your trigger crashing?
Its not. I have try / catch blocks in the trigger and the nested stored procedure to make sure if there IS a crash, it is handled and does not affect the modification of the table. To test it I have done stuff like insert a devide by zero to force a crash, and this is how I know my transaction batch is getting mashed, thereby loosing the table modification, regardless of my try catch blocks.quote: Triggers should be simple code, and thus relatively easy to debug.
I agree!! But then you say:quote: All the logic for a trigger should be encapsulated within the trigger, making the table somewhat like an OOP object which is sufficient and complete unto itself.
Hmmm. One of the core priniciples of Object Orientation design is encapsulation and code re-use. However you are suggesting that I avoid using a stored procedure and have all my code in the trigger. So the same code that deals with this auditing would then appear in every trigger on every table that needs auditing. If you then need to modify a trigger and its auditting code, you have to modify every single trigger. Im sorry, but thats not OO. Thats why I have the SP, which contains all the shared code that deals with the auditing process. |
 |
|
|
jen
Master Smack Fu Yak Hacker
4110 Posts |
Posted - 2006-04-11 : 20:47:31
|
| do you mean something like this?exec @run=sprocname(parameters...)if @run!=0 raiseerror...but i agree on creating it on the trigger and if you're incorporating dynamic sql in the sproc, this actually beats the purpose of optimization via execution plan, so you lose that benefitcreating it on the trigger reduces the risk that someone might just modify the sproc called withinthat's just me, your requirements might be different--------------------keeping it simple... |
 |
|
|
blindman
Master Smack Fu Yak Hacker
2365 Posts |
Posted - 2006-04-12 : 00:07:01
|
quote: Originally posted by OgreiteHmmm. One of the core priniciples of Object Orientation design is encapsulation and code re-use. However you are suggesting that I avoid using a stored procedure and have all my code in the trigger. So the same code that deals with this auditing would then appear in every trigger on every table that needs auditing.
Hmmm...have to <slightly> disagree with your definition. The benefit of code reuse in OOP is incidental to the goal of object reuse. And in any code, first and foremost the code and objects must be robust. Regardless, I would not try to push the TSQL "OOP" analogy too far. TSQL is by no means an object-oriented language. My reference was too OOP's principle of self-sufficient "black-box" objects. So instead of thinking of the trigger as an object, think of it as a method of the table. It's scope of control should be limited to the table.FYI, triggers are very useful tools and I use them frequently, but any of these gurus will tell you there are few faster ways to hose up your database than with improperly written or poorly controled triggers. |
 |
|
|
Ogreite
Starting Member
5 Posts |
Posted - 2006-04-12 : 04:43:35
|
| Thanks for the discussion guys. The fact that I am using an SP here (called from within the trigger) is actually irrelevant to my problem. Jen, you are right that this solution of mine is not the best in terms of optimization, but unfortunately I am forced to go down this route. I don’t pay my wages, my boss does ;)blindman, I respect your opinions but I think I’ll have to agree to disagree on that one.For the time being lets forget about the stored proc please. I was really after some more information about recovering from errors in a trigger. I want to make it robust so that if it crashes, I can report the error and continue, but also so that I do not loose the modifications that caused the trigger to fire in the first place.I can’t see why this wouldn’t be possible; after all, the AFTER trigger is fired once the transaction has been successfully applied. I guess I want a trigger that fires after a commit. Is there such a thing?If I force my trigger to crash for testing, I get the following message: “Transaction doomed in trigger. Batch has been aborted.”That means I have lost the changes that originally caused my trigger to fire e.g. inserting a new record into the table. So currently if your trigger crashes, you can’t catch the error and your transaction is always doomed. Is there no way around this? |
 |
|
|
holistic
Starting Member
4 Posts |
Posted - 2009-02-20 : 13:29:33
|
| ever found a solution to this isse? |
 |
|
|
panchimartin
Starting Member
2 Posts |
Posted - 2011-03-29 : 12:17:45
|
| You can commit the transaction in the trigger, before you do whatever you want to do. I tried in a test env and it seems to work for the transaction, but what gets ended is the batch:Msg 3609, Level 16, State 1, Line 1The transaction ended in the trigger. The batch has been aborted(2 years later...)=) |
 |
|
|
panchimartin
Starting Member
2 Posts |
Posted - 2011-03-29 : 12:34:36
|
| an addition to my previous post:after transaction is commited, tables inserted and deleted are not available any more!!!if you need them, you may try something like:select * into #i from insertedselect * into #d from deletedcommit tran-- your code here that use #i and #d in place of inserted and deletedIt works... but it is weird so i'm not sure i would use it.any comments? |
 |
|
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2011-03-30 : 11:59:44
|
quote: Originally posted by Ogreite Thanks for the discussion guys. The fact that I am using an SP here (called from within the trigger) is actually irrelevant to my problem. Jen, you are right that this solution of mine is not the best in terms of optimization, but unfortunately I am forced to go down this route. I don’t pay my wages, my boss does ;)blindman, I respect your opinions but I think I’ll have to agree to disagree on that one.For the time being lets forget about the stored proc please. I was really after some more information about recovering from errors in a trigger. I want to make it robust so that if it crashes, I can report the error and continue, but also so that I do not loose the modifications that caused the trigger to fire in the first place.I can’t see why this wouldn’t be possible; after all, the AFTER trigger is fired once the transaction has been successfully applied. I guess I want a trigger that fires after a commit. Is there such a thing?If I force my trigger to crash for testing, I get the following message: “Transaction doomed in trigger. Batch has been aborted.”That means I have lost the changes that originally caused my trigger to fire e.g. inserting a new record into the table. So currently if your trigger crashes, you can’t catch the error and your transaction is always doomed. Is there no way around this?
Your trigger is part of the transaction, not something that happens after the transaction is committed.Just make sure your code is so bulletproof that you don't get errors and you will be fine.As a general rule a trigger should be very simple, so calling a stored procedure in a trigger is usually a very bad idea. Too many things can go wrong, and the requirement to call it multiple times in a loop to process each row can really slow things down.CODO ERGO SUM |
 |
|
|
|
|
|
|
|