472,127 Members | 1,997 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,127 software developers and data experts.

Trigger performance

I have a multi-part question regarding trigger performance. First of
all, is there performance gain from issuing the following within a
trigger:

SELECT PrimaryKeyColumn FROM INSERTED

opposed to:

SELECT * FROM INSERTED

Secondly, what is the optimum way to determine which action fired an
AFTER trigger that is defined for INSERT, UPDATE, DELETE? Is it
something similiar to:

IF NOT EXISTS(SELECT * FROM DELETED)
--sql block for insert
ELSE
IF NOT EXISTS(SELECT * FROM INSERTED)
--sql block for delete
ELSE
--sql block for update

or is there a superior method?

Lastly, is checking @@ROWCOUNT at the beginning of an AFTER trigger the
best way to differentiate between multiple rows being affected or just
a single row? Can using this @@ROWCOUNT test fail? Are there any
situations where it would return erroneous results?

I realize I'm being somewhat nitpicky on these matters but any feedback
would be greatly appreciated!

Jul 23 '05 #1
6 6423
1.
SELECT PrimaryKeyColumn FROM INSERTED

SELECT * FROM INSERTED
Since those two statements presumably return different results (unless you
only have one colum) I'm not sure what you are trying to compare here. It's
good practice to avoid SELECT * except in an EXISTS subquery. Always list
the columns you require because it makes your code clearer and more
maintainable and it maximizes the chances for the optimizer to use indexes.
In an EXISTS subquery the opposite argument applies: *any* index may be used
to optimize an EXISTS subquery so listing the column names is unnecessary
and makes no difference to the logical meaning of the query.

2. Secondly, what is the optimum way to determine which action fired an
AFTER trigger that is defined for INSERT, UPDATE, DELETE?
Try to keep trigger logic simple and set-based - you may be able to write a
single DML statement rather than use multiple IFs in one trigger. If the
logic is quite different in each case then create separate triggers for each
action.

3. Lastly, is checking @@ROWCOUNT at the beginning of an AFTER trigger the
best way to differentiate between multiple rows being affected or just


Why would you care if one or multiple rows were updated? Triggers should use
set-based code so the logic should be identical in any case. Never assume
that only single rows will be updated - this is a classic error and one that
causes a lot of grief judging by posts in this group.

--
David Portas
SQL Server MVP
--
Jul 23 '05 #2
oj
1. yes. 'select keycol' returns much less data. thus, it's more efficient.
2. yes. you want to use if...then block for each type.

e.g.
declare @i int, @d int
select @i=count(*) from inserted
select @d=count(*) from deleted

if @i=@d
print 'update'
if @i>@d
print 'insert'
if @i<@d
print 'deleted'

3. yes. @@rowcount in after trigger would tell us if any rows have been
affected. 0 will be returned if no row is affected.
"Scott CM" <sc******@hotmail.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...
I have a multi-part question regarding trigger performance. First of
all, is there performance gain from issuing the following within a
trigger:

SELECT PrimaryKeyColumn FROM INSERTED

opposed to:

SELECT * FROM INSERTED

Secondly, what is the optimum way to determine which action fired an
AFTER trigger that is defined for INSERT, UPDATE, DELETE? Is it
something similiar to:

IF NOT EXISTS(SELECT * FROM DELETED)
--sql block for insert
ELSE
IF NOT EXISTS(SELECT * FROM INSERTED)
--sql block for delete
ELSE
--sql block for update

or is there a superior method?

Lastly, is checking @@ROWCOUNT at the beginning of an AFTER trigger the
best way to differentiate between multiple rows being affected or just
a single row? Can using this @@ROWCOUNT test fail? Are there any
situations where it would return erroneous results?

I realize I'm being somewhat nitpicky on these matters but any feedback
would be greatly appreciated!

Jul 23 '05 #3
> SELECT PrimaryKeyColumn FROM INSERTED

opposed to:

SELECT * FROM INSERTED
What for? You seldom want to return data from a trigger, so neither is
reall optimum. Can you explain what you are trying to do?
Secondly, what is the optimum way to determine which action fired an
AFTER trigger that is defined for INSERT, UPDATE, DELETE? Is it
something similiar to:
The optimum way to do this is to have three triggers, on for insert, update
and for delete. Then you don't have to spend resources determining which
kind of trigger it is, not to mention that the plan for the trigger will be
better if you don't have conditional execution of blocks:
IF NOT EXISTS(SELECT * FROM DELETED)
--sql block for insert
ELSE
IF NOT EXISTS(SELECT * FROM INSERTED)
--sql block for delete
ELSE
--sql block for update
If you must do this, this is adequate.
Lastly, is checking @@ROWCOUNT at the beginning of an AFTER trigger the
best way to differentiate between multiple rows being affected or just
a single row? Can using this @@ROWCOUNT test fail? Are there any
situations where it would return erroneous results?
Yes, it is the best way, but it has to be the FIRST statement (other than
declararation of variables) So you need to do:

AS
declare @rowcount int
set @rowcount = @@rowcount

Then you can check @rowcount. You can just check @@rowcount instead of the
declare, but I generally set the value to a variable, since the number of
rows affected by the trigger can often be useful later in the trigger.
--
----------------------------------------------------------------------------
Louis Davidson - dr***@hotmail.com
SQL Server MVP

Compass Technology Management - www.compass.net
Pro SQL Server 2000 Database Design -
http://www.apress.com/book/bookDisplay.html?bID=266
Note: Please reply to the newsgroups only unless you are interested in
consulting services. All other replies may be ignored :)

"Scott CM" <sc******@hotmail.com> wrote in message
news:11**********************@z14g2000cwz.googlegr oups.com...I have a multi-part question regarding trigger performance. First of
all, is there performance gain from issuing the following within a
trigger:

SELECT PrimaryKeyColumn FROM INSERTED

opposed to:

SELECT * FROM INSERTED

Secondly, what is the optimum way to determine which action fired an
AFTER trigger that is defined for INSERT, UPDATE, DELETE? Is it
something similiar to:

IF NOT EXISTS(SELECT * FROM DELETED)
--sql block for insert
ELSE
IF NOT EXISTS(SELECT * FROM INSERTED)
--sql block for delete
ELSE
--sql block for update

or is there a superior method?

Lastly, is checking @@ROWCOUNT at the beginning of an AFTER trigger the
best way to differentiate between multiple rows being affected or just
a single row? Can using this @@ROWCOUNT test fail? Are there any
situations where it would return erroneous results?

I realize I'm being somewhat nitpicky on these matters but any feedback
would be greatly appreciated!

Jul 23 '05 #4
Scott's posting didn't make it here (I found it on Google), so I'm adding
my replies to David's pos.

David Portas (RE****************************@acm.org) writes:
1.
SELECT PrimaryKeyColumn FROM INSERTED

SELECT * FROM INSERTED


Both of these seems wrong. Trigger should not normally return result sets.
Sometimes you want to do this for debugging, but leaving it around can
cause great confusion, because the client that executed the INSERT/UPDATE/
DElETE statment is not expecting a result set, and not consuming the
result set, can lead to the trigger not completing, the transaction not
being committed, and locks being held blocking other users.

All that said, I can't see any difference in effeciecy in this case,
since "inserted" is a virtual table, and there are no indexes.

And that leads to a question of trigger performance you didn't ask: if
your trigger makes several references to the inserted/deleted tables,
consider doing this first in your trigger:

INSERT @inserted (...)
SELECT ... FROM inserted

and then use the table variables in the following queries. The virtual
tables are slow, because they are read from the transaction log.

It might be tempting to do:

SELECT * FROM #inserted FROM inserted

since you don't have to declare a copy of the table, but since triggers
are always run in a transaction context, this can be costly, since the
creation of the temp table is logged. (Believe me, this is something I've
learnt the hard way.)
2.
Secondly, what is the optimum way to determine which action fired an
AFTER trigger that is defined for INSERT, UPDATE, DELETE?
Try to keep trigger logic simple and set-based - you may be able to
write a single DML statement rather than use multiple IFs in one
trigger. If the logic is quite different in each case then create
separate triggers for each action.


Scott's question is certainly meaningful. I often write a combined
INSERT/UPDATE trigger, because the same validations has to be performed.
For the same reason, I also include the DELETE action for the same trigger.
But some checks or cascading actions may only perform to one of them. In
that case it appears to be good for performance to not run that
check/action.

The way to check is:

IF EXISTS (SELECT * FROM @inserted) AND NOT (SELECT * FROM @deleted)
-- We have an INSERT statement.

3.
Lastly, is checking @@ROWCOUNT at the beginning of an AFTER trigger the
best way to differentiate between multiple rows being affected or just

Probably. Although, I prefer to check SELECT COUNT(*) on inserted/deleted.
This may be due to that in earlier versions of SQL Server, @@rowcount did
not always have the right number.
Why would you care if one or multiple rows were updated? Triggers should
use set-based code so the logic should be identical in any case. Never
assume that only single rows will be updated - this is a classic error
and one that causes a lot of grief judging by posts in this group.


There are indeed cases where you want to differentiate between a
singlerow/multirow operation:

o The primary key is updated, and you need to relate the old and new
value. Unless the table has an alternate key which is left unchanged,
you cannot handle multirow updates in this case. The one way out is
to disallow multi-row updates, by checking @@rowcount.

o The trigger logic for the general case for multi-row updates is much
slower than specific single-row logic, and you expect single-row
updates to be the most common. In this case you check @@rowcount,
and take different paths depending on the value. We have at least one
such trigger in our system.

o A variation of this is that the multi-row handling is complicated, and
there is only a requirement to handle single-row operation. After all,
most development budgets are limited. Of course, in this case too, you
need to check @@rowcount, and raise an error in case of a multi-row
operation.

--
Erland Sommarskog, SQL Server MVP, es****@sommarskog.se

Books Online for SQL Server SP3 at
http://www.microsoft.com/sql/techinf...2000/books.asp
Jul 23 '05 #5
On Fri, 10 Dec 2004 15:25:38 -0800, oj wrote:
2. yes. you want to use if...then block for each type.

e.g.
declare @i int, @d int
select @i=count(*) from inserted
select @d=count(*) from deleted

if @i=@d
print 'update'
if @i>@d
print 'insert'
if @i<@d
print 'deleted'


Hi oj,

I'm sorry, but I have to disagree with this part of your message. Using IF
EXISTS or IF NOT EXISTS is quicker thatn actually counting the number of
rows in inserted and deleted, so if you only need to distinguish between
insert, update and delete (as per the original question), you should not
use COUNT(*).

Best, Hugo
--

(Remove _NO_ and _SPAM_ to get my e-mail address)
Jul 23 '05 #6
oj
Yes, you probably gain some if this is a large set. Tks.

"Hugo Kornelis" <hugo@pe_NO_rFact.in_SPAM_fo> wrote in message
news:kc********************************@4ax.com...
On Fri, 10 Dec 2004 15:25:38 -0800, oj wrote:
2. yes. you want to use if...then block for each type.

e.g.
declare @i int, @d int
select @i=count(*) from inserted
select @d=count(*) from deleted

if @i=@d
print 'update'
if @i>@d
print 'insert'
if @i<@d
print 'deleted'


Hi oj,

I'm sorry, but I have to disagree with this part of your message. Using IF
EXISTS or IF NOT EXISTS is quicker thatn actually counting the number of
rows in inserted and deleted, so if you only need to distinguish between
insert, update and delete (as per the original question), you should not
use COUNT(*).

Best, Hugo
--

(Remove _NO_ and _SPAM_ to get my e-mail address)

Jul 23 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

8 posts views Thread by Jason | last post: by
3 posts views Thread by takilroy | last post: by
5 posts views Thread by Bob Stearns | last post: by
reply views Thread by leo001 | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.