|
|
|
SSC Rookie
      
Group: General Forum Members
Last Login: Thursday, April 18, 2013 8:11 AM
Points: 46,
Visits: 565
|
|
Lynn Pettis (9/5/2008)
I must not fully understand INSERT triggers, but I was always under the impression that if nothing was inserted, an insert trigger WON'T fire. I have never had to write an insert trigger to handle a zero row insert; multi-row inserts, yes. I guess this is something I am going to have to investigate. 
Took me ages before I realised this was the case (I only found out thanks to some odd problems on a customer db). The same is true of Update/Delete triggers.
If a tree falls in a forest & no-one hears it, it may or may not make a sound. But it will cause a trigger to fire :)
|
|
|
|
|
SSCertifiable
       
Group: General Forum Members
Last Login: Today @ 4:45 PM
Points: 5,297,
Visits: 7,240
|
|
Lynn Pettis (9/5/2008)
I must not fully understand INSERT triggers, but I was always under the impression that if nothing was inserted, an insert trigger WON'T fire. I have never had to write an insert trigger to handle a zero row insert; multi-row inserts, yes. I guess this is something I am going to have to investigate. 
As others have already poitned out, the trigger stil fires even if no rows are affected. That's why many developers make it a habit to start each trigger with this line:
IF @@ROWCOUNT = 0 RETURN; Do make sure that it's the very first line of the trigger, though. I once have spent way too long staring at mysterious results until I realised that I had put this statement after the SET NOCOUNT ON, so I checked the @@ROWCOUNT from that SET statement (which is always 1).
Hugo Kornelis, SQL Server MVP Visit my SQL Server blog: http://sqlblog.com/blogs/hugo_kornelis
|
|
|
|
|
SSC-Insane
         
Group: General Forum Members
Last Login: Today @ 8:51 PM
Points: 21,832,
Visits: 27,861
|
|
|
|
|
|
Ten Centuries
      
Group: General Forum Members
Last Login: Today @ 10:31 AM
Points: 1,150,
Visits: 1,860
|
|
Hugo Kornelis (9/5/2008)
That's why many developers make it a habit to start each trigger with this line: IF @@ROWCOUNT = 0 RETURN; Do make sure that it's the very first line of the trigger, though. DO NOT use this in a trigger to test if any rows need to be processed by the trigger.
Although this technique has been documented in independent books (e.g., Ken Henderson) the value could be zero from some other action performed by some other trigger. I.e., the value of @@ROWCOUNT does not always reflect the number of rows affected in the table that the trigger is attached to.
From BOL: Returns the number of rows affected by the last statement.
Trust me on this as I have had this experience!
(PHB) I think we should build an SQL database. (Dilbert) What color do you want that database? (PHB) I think mauve has the most RAM.
|
|
|
|
|
SSCertifiable
       
Group: General Forum Members
Last Login: Today @ 4:45 PM
Points: 5,297,
Visits: 7,240
|
|
JohnG (9/5/2008)
Hugo Kornelis (9/5/2008)
That's why many developers make it a habit to start each trigger with this line: IF @@ROWCOUNT = 0 RETURN; Do make sure that it's the very first line of the trigger, though. DO NOT use this in a trigger to test if any rows need to be processed by the trigger. Although this technique has been documented in independent books (e.g., Ken Henderson) the value could be zero from some other action performed by some other trigger. I.e., the value of @@ROWCOUNT does not always reflect the number of rows affected in the table that the trigger is attached to. From BOL: Returns the number of rows affected by the last statement.Trust me on this as I have had this experience!
Hi John,
Good point. Thanks for the warning.
I had completely forgotten about this because I never declare more than one trigger for the same action. Frankly, I never really understood why people want to have multiple triggers for an action; I always code all checks and changes required in a single trigger.
(And if you do have a single trigger, then you can safely check @@ROWCOUNT as the first statement in the trigger, as the last statement will THEN always be the statement that caused the trigger to fire.
Hugo Kornelis, SQL Server MVP Visit my SQL Server blog: http://sqlblog.com/blogs/hugo_kornelis
|
|
|
|
|
Ten Centuries
      
Group: General Forum Members
Last Login: Today @ 10:31 AM
Points: 1,150,
Visits: 1,860
|
|
I had completely forgotten about this because I never declare more than one trigger for the same action. Frankly, I never really understood why people want to have multiple triggers for an action; I always code all checks and changes required in a single trigger.
(And if you do have a single trigger, then you can safely check @@ROWCOUNT as the first statement in the trigger, as the last statement will THEN always be the statement that caused the trigger to fire.
Only if you have one and only one trigger in the entire database!
The trigger that was fired and thus sets @@ROWCOUNT to zero could be some DML operation in a trigger on another table. Note that @@ROWCOUNT is global and not scoped to the DML on the table. It is reset on DML within a trigger.
In my case I had a parent->child relationship (e.g., Master->Detail) that required a trigger on the parent table. The deletions from the child table were done via a CASCADE delete which then fired the required trigger on the child table.
So the trigger fired, it done some things, the last of which affected zero rows, then the trigger on the child table fired. Since the last DML operation in the prior trigger resulted in zero rows affected, the 2nd trigger didn't do anything as it erroneously was testing @@ROWCOUNT.
This was a bitch to track down as to why the trigger didn't work properly all of the time.
I repeat, DO NOT test @@ROWCOUNT at the beginning of a trigger. Properly check the number of rows in the related trigger tables.
(PHB) I think we should build an SQL database. (Dilbert) What color do you want that database? (PHB) I think mauve has the most RAM.
|
|
|
|
|
SSCertifiable
       
Group: General Forum Members
Last Login: Today @ 4:45 PM
Points: 5,297,
Visits: 7,240
|
|
JohnG (9/5/2008) Only if you have one and only one trigger in the entire database!
The trigger that was fired and thus sets @@ROWCOUNT to zero could be some DML operation in a trigger on another table. Note that @@ROWCOUNT is global and not scoped to the DML on the table. It is reset on DML within a trigger.
Hi John,
If a DML operation that causes a trigger to fire is started in a trigger, the flow of that trigger is interrupted and the other trigger gets executed first. Once that is finished, the first resumes. So when the second trigger starts, the @@ROWCOUNT will be equal to the number of rows affected by the DML operation in the first trigger that caused the second to fire, as expected.
I know this very well, because I have been heavily involved in maintaining a database that had lots of nested (and even recursive) trigger executions.
In my case I had a parent->child relationship (e.g., Master->Detail) that required a trigger on the parent table. The deletions from the child table were done via a CASCADE delete which then fired the required trigger on the child table.
So the trigger fired, it done some things, the last of which affected zero rows, then the trigger on the child table fired. Since the last DML operation in the prior trigger resulted in zero rows affected, the 2nd trigger didn't do anything as it erroneously was testing @@ROWCOUNT.
This was a bitch to track down as to why the trigger didn't work properly all of the time.
I repeat, DO NOT test @@ROWCOUNT at the beginning of a trigger. Properly check the number of rows in the related trigger tables.
Aaah, cascading deletes/updates. That is indeed a very good point. Yes, if you have those, you should indeed avoid testing @@ROWCOUNT, since the order in which the triggers fire is undefined.
So I guess I should back down and reuce my statement to "@@ROWCOUNT is safe if used as the first line of code in the trigger, if there is no more than one trigger for the operation, and if the table the trigger is defined upon is not involved on either side of a foreign key with cascading updates or deletes". (But in those cases, @@ROWCOUNT is better, because it's slightly faster than checking for existance of a row in the trigger tables, and potentially lots faster than counting those rows).
Hugo Kornelis, SQL Server MVP Visit my SQL Server blog: http://sqlblog.com/blogs/hugo_kornelis
|
|
|
|
|
SSCarpal Tunnel
       
Group: General Forum Members
Last Login: Today @ 8:01 AM
Points: 4,557,
Visits: 8,237
|
|
I always use IF EXISTS(select 1 from inserted) or deleted. It allows not to run "insert" part of INSERT/UPDATE/DELETE trigger only when there are actually inserted rows.
|
|
|
|
|
SSCarpal Tunnel
       
Group: General Forum Members
Last Login: Today @ 8:01 AM
Points: 4,557,
Visits: 8,237
|
|
icocks (9/5/2008) A further thing to bear in mind - if there's an Insert trigger on the table, then it will be fired in response to option 2, but not option 1. Depending on what it does (& how smart you've been in limiting the processing for a zero row insert) then there may be a further performance hit and/or lock escalation to take into account. Of course you may have good reasons for wanting it to fire for even an unsuccessful insert attempt.
In general I prefer to anti-join the value list back to the insert table since that's much more easily expandable for multi-row inserts & compound keys.
My opinion is that there's rarely a truly right or wrong answer to this kind of question - the real skill lies in understanding the strengths & weaknesses of each approach & then choosing the appropriate option for the system it's being applied to.
Look on what you are choosing from: 1. A solution which may cause errors in production environment but easy on resources; 2. A solution which is error safe but potentially causes some overhead.
Which one wold you choose for your car? Are those airbags an overhead? Have you ever been in a situation when you really needed it? Have any of you friends?
So when you choose a car - will you prefer the one without airbags? Why?
|
|
|
|
|
Mr or Mrs. 500
      
Group: General Forum Members
Last Login: Wednesday, January 25, 2012 8:14 AM
Points: 567,
Visits: 512
|
|
| Great stuff here everyone. Thanks OP!!!!! :D
|
|
|
|