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.

 All Forums
 SQL Server 2008 Forums
 Transact-SQL (2008)
 don't fire trigger if only one column changed

Author  Topic 

ferrethouse
Constraint Violating Yak Guru

352 Posts

Posted - 2012-01-10 : 16:47:20
Is there an efficient way to code this? I have a "students" table that has a field called "lastloggedin". There is a trigger on this table that I want to fire if any field other than "lastloggedin" changes. There are 50 columns in this table so I'd prefer not to do the whole...

if inserted.lastloggedin <> lastloggedin and
inserted.col1 = col1 and inserted.col2 = col2 etc. etc. etc.

Bustaz Kool
Master Smack Fu Yak Hacker

1834 Posts

Posted - 2012-01-10 : 19:19:35
There are a couple of trigger specific functions that might be of use to you. They are UPDATE() and COLUMNS_UPDATED(). The first takes a column name returns true if the column actually changed value. The second returns a bit map of the columns which have changed. Some find this a bit hard to work with. You can find details on each in BOL.
If neither of these strike your fancy, you can join and inspect the values in the inserted and deleted pseudo tables. I wouldn't recommend it but you could.

=================================================
Men shout to avoid listening to one another. -Miguel de Unamuno
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2012-01-11 : 01:26:47
quote:
Originally posted by Bustaz Kool

There are a couple of trigger specific functions that might be of use to you. They are UPDATE() and COLUMNS_UPDATED(). The first takes a column name returns true if the column actually changed value. The second returns a bit map of the columns which have changed. Some find this a bit hard to work with. You can find details on each in BOL.
If neither of these strike your fancy, you can join and inspect the values in the inserted and deleted pseudo tables. I wouldn't recommend it but you could.

=================================================
Men shout to avoid listening to one another. -Miguel de Unamuno


Not fully correct
It will return true even if column didnt actually change its value but still was a part of update. If just returns the status whether column was a part of update operation without actually looking into before and after values

------------------------------------------------------------------------------------------------------
SQL Server MVP
http://visakhm.blogspot.com/

Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2012-01-11 : 01:28:18
quote:
Originally posted by ferrethouse

Is there an efficient way to code this? I have a "students" table that has a field called "lastloggedin". There is a trigger on this table that I want to fire if any field other than "lastloggedin" changes. There are 50 columns in this table so I'd prefer not to do the whole...

if inserted.lastloggedin <> lastloggedin and
inserted.col1 = col1 and inserted.col2 = col2 etc. etc. etc.


you cant avoid trigger firing like that
Trigger will fire regardless ofthe condition
But inside trigger you can code checking logic and do processing only if any other field changes.


it will be like

IF EXISTS( SELECT 1 FROM DELETED d
INNER JOIN INSERTED i
ON i.PK = d.PK
AND i.Field1 <> d.Field1
AND i.Field2 <> d.Field2
AND i.Field3 <> d.Field3
AND...
)

your actual code

PK will your actual primary key and all fields except lastloggedin
has to to included in where in same format
------------------------------------------------------------------------------------------------------
SQL Server MVP
http://visakhm.blogspot.com/

Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2012-01-11 : 04:05:30
quote:
IF EXISTS( SELECT 1 FROM DELETED d
INNER JOIN INSERTED i
ON i.PK = d.PK
AND i.Field1 <> d.Field1
AND i.Field2 <> d.Field2
AND i.Field3 <> d.Field3
AND...
)



I don't think, in this sort of scenario, doing a test ahead of the actual code helps because there may be individual rows within the updated rows that satisfy the conditions, and other rows that don't, so I think you need to do it on the actual code (e.g. an INSERT or UPDATE), or if the code is more complex I would pre-select the PKs of the relevant rows into a @TableVar / #TempTable and JOIN that to other tables in the code.

Also, sadly, the test for "different" needs to be more complicated:

ON i.PK = d.PK
AND
(
(i.Field1 <> d.Field1 COLLATE Latin1_General_BIN
OR (i.Field1 IS NULL AND d.Field1 IS NOT NULL)
OR (i.Field1 IS NOT NULL AND d.Field1 IS NULL))
OR i.Field2 <> d.Field2 ...
)

leave off the COLLATE for any non-Char columns. You can leave the PK columns out of the tests (but including them, for completeness and future-proofing against maintenance-bugs will do no harm - they are, after all, going to match)

These WHERE clauses are a bit tedious to make so I generate them mechanically
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2012-01-11 : 04:12:09
quote:
Originally posted by Kristen

quote:
IF EXISTS( SELECT 1 FROM DELETED d
INNER JOIN INSERTED i
ON i.PK = d.PK
AND i.Field1 <> d.Field1
AND i.Field2 <> d.Field2
AND i.Field3 <> d.Field3
AND...
)



I don't think, in this sort of scenario, doing a test ahead of the actual code helps because there may be individual rows within the updated rows that satisfy the conditions, and other rows that don't, so I think you need to do it on the actual code (e.g. an INSERT or UPDATE), or if the code is more complex I would pre-select the PKs of the relevant rows into a @TableVar / #TempTable and JOIN that to other tables in the code.

Also, sadly, the test for "different" needs to be more complicated:

ON i.PK = d.PK
AND
(
(i.Field1 <> d.Field1 COLLATE Latin1_General_BIN
OR (i.Field1 IS NULL AND d.Field1 IS NOT NULL)
OR (i.Field1 IS NOT NULL AND d.Field1 IS NULL))
OR i.Field2 <> d.Field2 ...
)

leave off the COLLATE for any non-Char columns. You can leave the PK columns out of the tests (but including them, for completeness and future-proofing against maintenance-bugs will do no harm - they are, after all, going to match)

These WHERE clauses are a bit tedious to make so I generate them mechanically


It will help to identify if at all there are some rows which satisfies the criteria ops interested in though if you want to do some processing over each of them, you need to do in inline code just like in the above suggestion

------------------------------------------------------------------------------------------------------
SQL Server MVP
http://visakhm.blogspot.com/

Go to Top of Page

Bustaz Kool
Master Smack Fu Yak Hacker

1834 Posts

Posted - 2012-01-11 : 11:45:17
If I am correct im assuming that you want to avoid the condition where the only column that changed is a single specific "administrative" column then I'd use the COLUMNS_UPDATED() function. You'll need to figure out the correct mapping of your special column to the bit map but the test becomes does the bit map equal a specific value(s).

Visakh16, thanks for the elaboration on the UPDATE() function.

=================================================
Men shout to avoid listening to one another. -Miguel de Unamuno
Go to Top of Page

visakh16
Very Important crosS Applying yaK Herder

52326 Posts

Posted - 2012-01-11 : 11:49:13
quote:
Originally posted by Bustaz Kool

If I am correct im assuming that you want to avoid the condition where the only column that changed is a single specific "administrative" column then I'd use the COLUMNS_UPDATED() function. You'll need to figure out the correct mapping of your special column to the bit map but the test becomes does the bit map equal a specific value(s).

Visakh16, thanks for the elaboration on the UPDATE() function.

=================================================
Men shout to avoid listening to one another. -Miguel de Unamuno


no probs
you're welcome

------------------------------------------------------------------------------------------------------
SQL Server MVP
http://visakhm.blogspot.com/

Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2012-01-11 : 12:30:03
ummmm, what does your TRIGGER DO?

Do you only allow stored procedures to access or modify your data?



Brett

8-)

Hint: Want your questions answered fast? Follow the direction in this link
http://weblogs.sqlteam.com/brettk/archive/2005/05/25/5276.aspx


Want to help yourself?

http://msdn.microsoft.com/en-us/library/ms130214.aspx

http://weblogs.sqlteam.com/brettk/

http://brettkaiser.blogspot.com/


Go to Top of Page

ferrethouse
Constraint Violating Yak Guru

352 Posts

Posted - 2012-01-23 : 11:32:32
quote:
Originally posted by X002548

ummmm, what does your TRIGGER DO?

Do you only allow stored procedures to access or modify your data?



Students get automatically enrolled into courses based on their profile data. So if a student is a "Manager" then they may get assigned some management courses. Therefore, when a student's profile changes we need to check against the thousands of rules that may exist. But tens of thousands of students simply login to our application every day and this causes the "lastloggedin" date to change which causes all of these rules to be evaluated. It is unnecessary burden on the server.
Go to Top of Page

ferrethouse
Constraint Violating Yak Guru

352 Posts

Posted - 2012-01-23 : 15:53:27
Found this which seems to do the job...


declare @bit int ,
@field int ,
@doCode bit,
@char int
select @field = 0
set @doCode = 0
while @field < (select max(colid) from syscolumns where id = (select id from sysobjects where name = 'students'))
begin
select @field = @field + 1
select @bit = (@field - 1 )% 8 + 1
select @bit = power(2,@bit - 1)
select @char = ((@field - 1) / 8) + 1
--select @char, @field, @bit
if substring(COLUMNS_UPDATED(),@char, 1) & @bit > 0
BEGIN
if (select name from syscolumns where colid = @field and id = (select id from sysobjects where name = 'students')) <> 'LastLoggedIn'
set @doCode = 1
else
set @doCode = 0
END
END
Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2012-01-23 : 16:00:37
As already stated "COLUMNS_UPDATED" is NOT an indication that a column has changed - assuming that is important to you?

I would also be extremely concerned about using a LOOP in this way, particularly in a TRIGGER which is a time-critical piece of code.

Why can't you just compare all (relevant) columns between INSERTED and DELETED to see if any of the column(s) [you are concerned about] has changed? That's a set based operation and will be much more efficient.
Go to Top of Page

LoztInSpace
Aged Yak Warrior

940 Posts

Posted - 2012-01-24 : 01:18:51
Can you remove "lastloggedin" from the table and put it in a new table or somewhere else that already exists with a one/one on the students table?
You can create a view that joins the original table to the "somewhere else.lastloggedin" if you find you're using it a lot.
If you're not prepared to do the above then I'd say you just have to suck it up and type it in. Kristen's suggestion to mechanically generate the check code for you seems reasonable - you can just put the list of columns into Excel or something, work some magic then copy & paste into your trigger. Should't take more than 10 mins, but very ugly.

This is a classic example of why I hate triggers!
Go to Top of Page
   

- Advertisement -