Re: History functionality

From: Michel Cadot <micadot{at}altern{dot}org>
Date: Sat, 13 Mar 2004 11:06:59 +0100
Message-ID: <4052dd10$0$310$636a15ce_at_news.free.fr>


"Ed Avis" <ed_at_membled.com> a écrit dans le message de news:l1smgk4pph.fsf_at_budvar.future-i.net...
> "Michel Cadot" <micadot{at}altern{dot}org> writes:
>
> >>Now i need to have a history funtionality for a few of my
> >>tables. This must include the old values, time and date of change
> >>and user who changed the value.
>
> >Have a look at triggers:
>
> Yes, triggers are the way to do it. But it seems that wanting history
> on tables is very common and perhaps even the main use of triggers.
> Given a table definition it is possible to generate a history table
> and triggers to update the history. Is there a program to generate
> these definitions automatically?
>
> --
> Ed Avis <ed_at_membled.com>

I don't know such a program but i write the followng one in a couple of hours. I admit it is not very efficient and it does not work for long, user defined types, object types and so long. Feel free to enhance it. I don't test for 9i and timestamp column in the log table.

Def TABLE = &1

Set echo OFF
Set feedback OFF
Set heading OFF
Set linesize 2000
Set long 32760
Set newpage 0
Set pagesize 0
Set recsep OFF
Set space 0
Set trimout ON
Set trimspool ON
BTitle OFF
TTitle OFF
Set serveroutput on size 100000 format wrap

Set termout off

Col tab noprint new_value TABLE
Col log noprint new_value TABLE_LOG
Col trg noprint new_value TABLE_TRG
Col seq noprint new_value TABLE_SEQ
Col v   noprint new_value version
Select upper('&TABLE') tab,
       substr('&TABLE',1,26)||'_LOG' log,
       substr('&TABLE',1,23)||'_LOGTRG' trg,
       substr('&TABLE',1,23)||'_LOGSEQ' seq, v
from (select to_number(substr (banner, instr (banner, 'Release') + 8, 1)) v
      from v$version where banner like 'Oracle%');
Set termout on

Spool GenLog_&TABLE

Declare

   /* Cursor for table columns */
   type col_cursor is ref cursor;
   c_col col_cursor;

   rcol_cur  user_tab_columns.column_name%type;
   rcol_old  user_tab_columns.column_name%type;
   rcol_new  user_tab_columns.column_name%type;
   rcol_type varchar2(255);

   /* 8i cursor */
   c_col8 varchar2(4000) :=
   'Select col.column_name col_cur,
           ''OLD_''||substr(col.column_name,1,26) col_new,
           ''NEW_''||substr(col.column_name,1,26) col_old,
           col.data_type||
       decode(col.data_type,
              ''NUMBER'', decode(col.data_precision,
                                 NULL, decode(col.data_scale, NULL, '''', ''(38)''),
                                 ''(''||col.data_precision||
                                        decode(col.data_scale, NULL, '')'',
                                               '',''||col.data_scale||'')'')),
              ''CHAR'', ''(''||col.data_length||'')'',
              ''VARCHAR2'', ''(''||col.data_length||'')'',
              ''NCHAR'', ''(''||col.data_length||'')'',
              ''NVARCHAR2'', ''(''||col.data_length||'')'',
              ''RAW'', ''(''||col.data_length||'')''
              ) col_type

   from user_tab_columns col
   where col.table_name = :tabname
   order by col.column_id';
   /* 9i cursor */
   c_col9 varchar2(4000) :=
   'Select col.column_name col,
           ''OLD_''||substr(col.column_name,1,26) col_new,
           ''NEW_''||substr(col.column_name,1,26) col_old,
           col.data_type||
              ''NUMBER'', decode(col.data_precision,
                                 NULL, decode(col.data_scale, NULL, '''', ''(38)''),
                                 ''(''||col.data_precision||
                                        decode(col.data_scale, NULL, '')'',
                                               '',''||col.data_scale||'')'')),
              ''CHAR'', ''(''||col.char_length||'' ''||
                               decode(col.char_used,''B'',''BYTE'',''CHAR'')||'')'',
              ''VARCHAR2'', ''(''||col.char_length||'' ''||
                                   decode(col.char_used,''B'',''BYTE'',''CHAR'')||'')'',
              ''NCHAR'', ''(''||col.char_length||'' ''||
                                decode(col.char_used,''B'',''BYTE'',''CHAR'')||'')'',
              ''NVARCHAR2'', ''(''||col.char_length||'' ''||
                                    decode(col.char_used,''B'',''BYTE'',''CHAR'')||'')'',
              ''RAW'', ''(''||col.data_length||'')''
              ) col_type

   from user_tab_columns col
   where col.table_name = :tabname
   order by col.column_id';
   c_colt varchar2(4000);

   /* Table parameters */
   tabname user_tables.table_name%type;

Begin

   /* Initialisations */
   tabname := '&TABLE';
   if &version < 9 then c_colt := c_col8; else c_colt := c_col9; end if;    dbms_output.put_line (' ');

   /* Log sequence creation */

   dbms_output.put_line ('create sequence &TABLE_SEQ start with 0 increment by 1');
   dbms_output.put_line ('   minvalue 0 nomaxvalue nocycle cache 1000 order;');
   dbms_output.put_line (' ');

   /* Log table creation */
   dbms_output.put_line ('create table &TABLE_LOG (');    dbms_output.put_line (' id number(38),');    if &version < 9 then

      dbms_output.put_line (' time date,');    else

      dbms_output.put_line (' time timestamp,');    end if;
   dbms_output.put_line (' username varchar2(30),');    dbms_output.put_line (' action varchar2(30),');    open c_col for c_colt using tabname;
   loop

      fetch c_col into rcol_cur, rcol_old, rcol_new, rcol_type;
      exit when c_col%NOTFOUND;
      dbms_output.put_line ('   '||rcol_old||' '||rcol_type||',');
      dbms_output.put_line ('   '||rcol_new||' '||rcol_type||',');
   end loop;
   close c_col;
   dbms_output.put ('   constraint '||substr('&TABLE',1,23)||'_LOG_PK');
   dbms_output.put_line (' primary key (id));');
   dbms_output.put_line (' ');

   /* Trigger creation */

   dbms_output.put_line ('create or replace trigger &TABLE_TRG');
   dbms_output.put_line ('after insert or update or delete on &TABLE');
   dbms_output.put_line ('for each row');
   dbms_output.put_line ('begin');
   dbms_output.put_line ('   if inserting then');
   dbms_output.put_line ('      insert into &TABLE_LOG (');
   dbms_output.put_line ('         id, time, username, action');
   open c_col for c_colt using tabname;
   loop
      fetch c_col into rcol_cur, rcol_old, rcol_new, rcol_type;
      exit when c_col%NOTFOUND;
      dbms_output.put_line ('        ,'||rcol_new);
   end loop;
   close c_col;
   dbms_output.put_line (' ) values (');    if &version < 9 then

      dbms_output.put_line (' &TABLE_SEQ..nextval, sysdate, user, ''INSERT''');    else

      dbms_output.put_line (' &TABLE_SEQ..nextval, systimestamp, user, ''INSERT''');    end if;
   open c_col for c_colt using tabname;
   loop

      fetch c_col into rcol_cur, rcol_old, rcol_new, rcol_type;
      exit when c_col%NOTFOUND;
      dbms_output.put_line ('        ,:new.'||rcol_cur);
   end loop;
   close c_col;

      dbms_output.put_line (' );');

   dbms_output.put_line ('   elsif deleting then');
   dbms_output.put_line ('      insert into &TABLE_LOG (');
   dbms_output.put_line ('         id, time, username, action');
   open c_col for c_colt using tabname;
   loop
      fetch c_col into rcol_cur, rcol_old, rcol_new, rcol_type;
      exit when c_col%NOTFOUND;
      dbms_output.put_line ('        ,'||rcol_old);
   end loop;
   close c_col;
   dbms_output.put_line (' ) values (');    if &version < 9 then

      dbms_output.put_line (' &TABLE_SEQ..nextval, sysdate, user, ''DELETE''');    else

      dbms_output.put_line (' &TABLE_SEQ..nextval, systimestamp, user, ''DELETE''');    end if;
   open c_col for c_colt using tabname;
   loop

      fetch c_col into rcol_cur, rcol_old, rcol_new, rcol_type;
      exit when c_col%NOTFOUND;
      dbms_output.put_line ('        ,:old.'||rcol_cur);
   end loop;
   close c_col;

      dbms_output.put_line (' );');

   dbms_output.put_line ('   else /* updating */');
   dbms_output.put_line ('      insert into &TABLE_LOG (');
   dbms_output.put_line ('         id, time, username, action');
   open c_col for c_colt using tabname;
   loop
      fetch c_col into rcol_cur, rcol_old, rcol_new, rcol_type;
      exit when c_col%NOTFOUND;
      dbms_output.put_line ('        ,'||rcol_old);
      dbms_output.put_line ('        ,'||rcol_new);
   end loop;
   close c_col;
   dbms_output.put_line (' ) values (');    if &version < 9 then

      dbms_output.put_line (' &TABLE_SEQ..nextval, sysdate, user, ''UPDATE''');    else

      dbms_output.put_line (' &TABLE_SEQ..nextval, systimestamp, user, ''UPDATE''');    end if;
   open c_col for c_colt using tabname;
   loop

      fetch c_col into rcol_cur, rcol_old, rcol_new, rcol_type;
      exit when c_col%NOTFOUND;
      dbms_output.put_line ('        ,:old.'||rcol_cur);
      dbms_output.put_line ('        ,:new.'||rcol_cur);
   end loop;
   close c_col;

      dbms_output.put_line (' );');

   dbms_output.put_line ('   end if;');
   dbms_output.put_line ('end;');
   dbms_output.put_line ('/');
   dbms_output.put_line (' ');

End;
/

Spool off

Regards
Michel Cadot Received on Sat Mar 13 2004 - 11:06:59 CET

Original text of this message