views:

428

answers:

10

Suppose I have a database table that has a timedate column of the last time it was updated or inserted. Which would be preferable:

  1. Have a trigger update the field.
  2. Have the program that's doing the insertion/update set the field.

The first option seems to be the easiest since I don't even have to recompile to do it, but that's not really a huge deal. Other than that, I'm having trouble thinking of any reasons to do one over the other. Any suggestions?

+7  A: 

The first option can be more robust because the database will be maintaining the field. This comes with the possible overhead of using triggers.

If you could have other apps writing to this table in the future, via their own interfaces, I'd go with a trigger so you're not repeating that logic anywhere else.

If your app is pretty much it, or any other apps would access the database through the same datalayer, then I'd avoid that nightmare that triggers can induce and put the logic directly in your datalayer (SQL, ORM, stored procs, etc.).

Of course you'd have to make sure your time-source (your app, your users' pcs, your SQL server) is accurate in either case.


Regarding why I don't like triggers:

Perhaps I was rash by calling them a nightmare. Like everything else, they are appropriate in moderation. If you use them for very simple things like this, I could get on board.

It's when the trigger code gets complex (and expensive) that triggers start to cause lots of problems. They are a hidden tax on every insert/update/delete query you execute (depending on the type of trigger). If that tax is acceptable then they can be the right tool for the job.

Michael Haren
Would you be able to elaborate on the "nightmare that triggers can induce"? I haven't really worked with triggers that much, so it'd be nice to know what I'm getting into.
Jason Baker
1. When you introduce a trigger, you're assuming that a very general triggering condition will always necessarily justify and require the same action. And it's very hard to predict all the possible circumstances that might set off the trigger. 2. Exception handling is hell. 3. Debugging is hell.
le dorfier
also triggers can wreak hell with @@identity which some insert code can depends on, +1 for use in moderation where appropriate, be careful of subtle locking issues
Sam Saffron
Triggers are hidden side effects. It's very easy to forget or for new team members to be unaware of these effects and write bad code as a result.
Joel Coehoorn
That's a great point @Joel.
Michael Haren
A reason why @@IDENTITY must be replaced by SCOPE_IDENTITY() in my projects...
devstuff
If you also wanted a "LastModifiedBy' column, the trigger approach is only viable if you are not using connection pooling....
Mitch Wheat
+4  A: 

I would say trigger just in case that someone uses something besides your app to update the table, you probably also want to have a LastUpdatedBy and use SUSER_SNAME() for that, this way you can see who did the update

SQLMenace
If you're using connection pooling, SUSER_SNAME() isn't going to be that informative..!
Mitch Wheat
And if someone else's app is updating the table, how would you anticipate it will be for a reason compatible with the intention of your field? And downside, they could have no clue what they were causing to happen. Unintended Consequences are evil.
le dorfier
+4  A: 

As long as I'm using a DBMS in whose triggers I trust, I'd always go with the trigger option. It allows the DBMS to take care of as many things as possible, which is usually a good thing.

It work make sure under any circumstances that the timestamp column has the correct value. The overhead would be negligible.

The only thing that would be against triggers is portability. If that's not an issue, I don't think there is a question which direction to go.

IronGoofy
Disagree - DBMS should be responsible for data structural integrity only; never application integrity (which this would be).
le dorfier
+2  A: 

Normally I'd say do it database side, but it depends on your application. If you're using LINQ-to-SQL you can just set the field as Timestamp and have your DAL use the Timestamp field for concurrency. It handles it for you automatically, so having to repeat code is a non event.

If you're writing your DAL yourself though, then I'd be more likely to handle this on the database side as it makes writing user interfaces far more flexible - although, I'd likely do this in a stored procedure that has "public" access and the tables locked down - you don't want just any clown coming along and bypassing your stored procedure by writing to the tables directly... unless you plan on making your DAL a standalone component that any future application must use to access the database, in which case, you could code it directly into the DAL - of course, you should only do this if you can guarantee that everyone accessing the database is doing so through your DAL component.

If you're going to allow "public" access to the database to insert into tables, then you'll have to go with the trigger because otherwise anyone can insert/update a single field in the table and the updated field could never get updated.

BenAlabaster
Interesting. Didn't know about the timestamp field.
Jason Baker
+2  A: 

I would have the date maintained at the database, i.e., a trigger, stored procedure, etc. In most of your database-driven applications the user app is not going to be the only means by which the business users get at data. There are reporting tools, extracts, user SQL, etc. There's also updates and corrections that are done by the DBA that the application won't be providing the date for as well.

But honestly the #1 reason I wouldn't do it from the application is you have no control over the date/time on the client machine. They might be rolling it back to get more days out of a trial license on something or may just want to do bad things to your program.

esabine
+1  A: 

You can do this without the trigger if your database supports default values on the fields. For example, in SQL Server 2005 I have a table with a field created like this:

create table dbo.Repository
   (
    ...
   last_updated     datetime default getdate(),
    ...
   )

then the insert code just leaves that field out of the insert field list.

I forgot that only worked for the first insert - I do have an update trigger as well, to update the date fields and put a copy of the updated record in my history table - which I would post ... but the editor keeps erroring out on my code ...

Finally:

create trigger dbo.Repository_Upd on dbo.Repository instead of update
as
--**************************************************************************
--   Trigger: Repository_Upd
--    Author: Ron Savage
--      Date: 09/28/2008
--
-- Description:
-- This trigger sets the last_updated and updated_by fields before the update
-- and puts a copy of the updated row into the Repository_History table.
--
-- Modification History:
-- Date        Init  Comment
-- 10/22/2008  RS    Blocked .prm files from updating the history as they
--                   get updated every time the cfg file is run.
-- 10/21/2008  RS    Updated the insert into the history table to use the
--                   d.last_updated field from the Repository table rather
--                   than getdate() to avoid micro second differences.
-- 09/28/2008  RS    Created.
--**************************************************************************
begin
   --***********************************************************************
   -- Update the record but fill in the updated_by, updated_system and
   -- last_updated date with current information.
   --***********************************************************************
   update cr set
      cr.filename           = i.filename,
      cr.created_by         = i.created_by,
      cr.created_system     = i.created_system,
      cr.create_date        = i.create_date,
      cr.updated_by         = user,
      cr.updated_system     = host_name(),
      cr.last_updated       = getdate(),
      cr.content            = i.content
   from
      Repository cr

      JOIN Inserted i
         on (i.config_id = cr.config_id);


   --***********************************************************************
   -- Put a copy in the history table
   --***********************************************************************
   declare @extention varchar(3);
   select @extention = lower(right(filename,3)) from Inserted;

   if (@extention <> 'prm')
      begin
      Insert into Repository_History
         select
            i.config_id,
            i.filename,
            i.created_by,
            i.created_system,
            i.create_date,
            user           as updated_by,
            host_name()    as updated_system,
            d.last_updated,
            d.content
         from
            Inserted i

            JOIN Repository d
               on (d.config_id = i.config_id);
      end
end

Ron

Ron Savage
But only the first time.
le dorfier
So true ... I do use a trigger for updates. :-)
Ron Savage
+6  A: 

You didn't mention 3. Use a stored procedure to update the table. The procedure can set timestamps as desired.

Perhaps that's not feasible for you, but I didn't see it mentioned.

StuffMaster
I consider it a preferred alternative, since triggers are at best appropriate for data integrity enforcement; which this aint.
le dorfier
I assumed that the SP method was covered by option 2.
Michael Haren
+3  A: 

I'm a proponent of stored procedures for everything. Your update proc could contain a GETDATE() for the column.

And I don't like triggers for this kind of update. Lack of visibility of triggers tends to cause confusion.

LJ
+3  A: 

This sounds like business logic to me ... I would be more disposed to putting this in the code. Let the database manage the storage of data ... No more and no less.

mattruma
+3  A: 

Triggers are a blessing and a curse.

Blessing: You can use them to enable all kinds of custom constraint checking and data management without backend systems knowledge or changes.

Curse: You don't know whats happening behind your back. Concurrency issues/deadlocks by additional objects brought into transactions that were not origionally expected. Phantom behavior including session environment changes, unreliable rowcounts. Excessive triggering of conditions..additional hotspot/performance penalties.

The answer to this question (Update dates implicitly(trigger) or explicitly (code)) ususally weights heavily on context. For example if you are using last change date as an informational field you might want to only change it when a 'user' actually makes salient changes to a row vs an automated process that simply updates some sort of internal marker users don't care about.

If you are using the trigger for change synchronization or you have no control over code that is executing a trigger makes a lot more sense.

My advise on trigger use it to be careful. Most systems allow you to filter execution based on the operation and fields changed. Proper use of 'before' vs 'after' triggers can have a significant performance impacts.

Finally a few systems are capable of executing a single trigger on multiple changes (multiple rows effected within a transaction) your code should be prepared to apply itself as a bulk update to multiple rows.

Einstein