tags:

views:

591

answers:

6

Is there a way to create a timestamp column in Oracle that automatically stores a timestamp of when the record has changed ?

+4  A: 

Pretty sure you have to do this with a trigger in Oracle:

create or replace TRIGGER parkedorder_tbiur
   BEFORE INSERT OR UPDATE
   ON parkedorder
   REFERENCING OLD AS old_row NEW AS new_row
   FOR EACH ROW
BEGIN
   IF INSERTING
   THEN
      IF :new_row.ID IS NULL
      THEN
         SELECT parkedorder_seq.NEXTVAL
           INTO :new_row.ID
           FROM DUAL;
      END IF;
   END IF;

   IF    :new_row.lastupdated <> SYSDATE
      OR :new_row.lastupdated IS NULL
   THEN
      SELECT sysdate
        INTO :new_row.lastupdated
        FROM DUAL;
   END IF;

   SELECT SYS_CONTEXT ( 'USERENV', 'OS_USER' )
     INTO :new_row.lastupdatedby
     FROM DUAL;
END;
dkackman
clever trigger. for the edification of future generations, this appears to do 3 things. It sets the primary key to a sequential ID, it sets the last updated time to the current time, and it sets "last updated by" to some mysterious thing I've never seen before, but I gather it gets a username somehow. +1
rmeador
Yeah it sets the lastupdatedby column to the username of the credentials used on the client to initiate the update; which for this app is the interactive user on the client.
dkackman
You don't need the second if. You are inserting so just set the lastupdated value. The <> SYSDATE is extremely prone to localtime vs servertime errors if you allow the user doing the insert to override the lastupdated field. Far better to enforce the lastupdated value unconditionally. Also, you can combine that sql statement with the last one to reduce the number of statements inside the trigger.
jmucchiello
+2  A: 

This is an interesting question. For oracle i usually use a trigger to update the timestamp field

CREATE OR REPLACE TRIGGER update_timestamp 
  BEFORE INSERT OR UPDATE ON some_table
BEGIN
  :NEW.TS := systimestamp;
END;

Oracle does not seem to have a built-in attribute for updating the timestamp field to the current timestamp (unlike other DBs like MySQL).

devpl
+1  A: 

Yes, via a trigger:

create or replace
TRIGGER schema.name_of_trigger
BEFORE INSERT OR UPDATE ON schema.name_of_table
FOR EACH ROW
BEGIN
    :new.modified_on := SYSTIMESTAMP;
END;

This assumes your table has a field called modified_on.

As has been noted above, a trigger is an ideal candidate anytime you have multiple different places where the table gets updated. If you only have one function/procedure that can update the table, just do it there, and skip the trigger.

David Oneill
+3  A: 

Tables I've modelled always include:

  • CREATED_USER, VARCHAR2
  • CREATED_DATE, DATE
  • UPDATED_USER, VARCHAR2
  • UPDATED_DATE, DATE

...columns. Why implement a trigger when you can set the value at the same time as the INSERT/UPDATE?

INSERT INTO TABLE (...CREATED_DATE, UPDATED_DATE) VALUES (...,SYSDATE, SYSDATE);

UPDATE TABLE
   SET ...,
       UPDATED_DATE = SYSDATE
OMG Ponies
Because it's really easy to forget to do that, and with a trigger, you can't forget. It comes down to the environment. If you're never changing the SQL that's accessing the data, you're right a trigger is a waste. But in most cases, it's far easier to have a trigger than *always* remember to set it
David Oneill
what data type should the sysdate be stored as?
Ayrad
OMG Ponies
@David: I'd only allow a single stored procedure to ever update a given table, then reference the sproc as necessary. Triggers are far easier to miss because their presence *isn't* visible when viewing sprocs.
OMG Ponies
rexem: Because the trigger will always work even when some gung ho user who shouldn't have direct access to the table somehow acquires it (usually through social engineering) and they use this access to add records outside of your sproc. Once the database is in the wild you have less control.
jmucchiello
If a user has direct access, you have bigger problems.
OMG Ponies
@rexem: gotcha. That makes sense: as long as you have tight enough control to be confident that nothing extra is sneaking in. If you do: good work! Our tables end up being edited from all kinds of different places, so we use triggers...
David Oneill
@jmucchiello yeah, if a user has direct access, that's even scarier then having a bunch of different pieces of code that can change it.
David Oneill
@David: The created and time are very easy to remember: use a not null constraint.
FerranB
+2  A: 

You can get very close to this by querying ORA_ROWSCN: http://download.oracle.com/docs/cd/B19306%5F01/server.102/b14200/pseudocolumns007.htm#sthref825

This is more accurate if you created the table with the ROWDEPENDENCIES option.

It actually logs the commit time for the record ...

drop table tester 
/

create table tester (col1 number, col2 timestamp)
rowdependencies
/

insert into tester values (1, systimestamp)
/

(approximate five second pause)

commit
/

select t.ora_rowscn,
       SCN_TO_TIMESTAMP(t.ora_rowscn),
       t.col1,
       t.col2
from   tester t
/

ORA_ROWSCN             SCN_TO_TIMESTAMP(T.ORA_ROWSCN) COL1                   COL2
---------------------- ------------------------------ ---------------------- -------------------------
9104916600628          2009-10-26 09.26.38.000000000  1                      2009-10-26 09.26.35.109848000
David Aldridge
A: 

Another way to deal with this is by turning on fine-grained audit. The individual rows won't have a timestamp, but you'll have a record of all changes. Overkill in most situations, though -- I usually just use triggers.

If you are OK with nearest .01 seconds, you can use date format and assign sysdate. If you need more detail, use the timestamp.

Jim Hudson
Oracle's date format is only accurate to the nearest 1 second. You need timestamp for sub-second accuracy.
Jeffrey Kemp