Skip to Main Content
  • Questions
  • Is there a way to identify which column of the row got updated in a given table

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Santhi.

Asked: April 19, 2020 - 10:45 am UTC

Last updated: April 22, 2020 - 3:58 am UTC

Version: Oracle Data Base 12C

Viewed 10K+ times! This question is

You Asked

We have a requirement to extract data of a table, if a particular column of a record gets updated. We can find the updated row of a table using 'last update date' column. Is there a way to find which column of that row got updated?

and Chris said...

The best way to do this is to enable some form of auditing that captures the old state of the table.

If you haven't done this and the update was "recently", then you can use flashback query to compare the old and new states of the table:

create table t ( c1 primary key, c2, c3, c4 ) as 
  select level, 'large text', sysdate, systimestamp
  from   dual
  connect by level <= 100;

exec dbms_lock.sleep ( 5 );

select current_scn from v$database;

CURRENT_SCN   
      28444485 

update t
set    c2 = 'new text'
where  c1 = 1;

update t
set    c3 = sysdate + 1
where  c1 = 2;

commit;

select t.*, versions_operation, versions_startscn scn_from, versions_endscn scn_to
from   t
  versions between scn 28444485 and maxvalue
where  c1 = 1;

C1    C2            C3                      C4                          VERSIONS_OPERATION       SCN_FROM      SCN_TO     
    1 new text      21-APR-2020 08:23:59    21-APR-2020 08.23.59 +00    U                        28444494      <null> 
    1 large text    21-APR-2020 08:23:59    21-APR-2020 08.23.59 +00    <null>                     <null>    28444494 


select t.*, versions_operation, versions_startscn scn_from, versions_endscn scn_to
from   t
  versions between scn 28444485 and maxvalue
where  c1 = 2;

C1    C2            C3                      C4                          VERSIONS_OPERATION       SCN_FROM      SCN_TO     
    2 large text    22-APR-2020 08:24:07    21-APR-2020 08.23.59 +00    U                        28444494      <null> 
    2 large text    21-APR-2020 08:23:59    21-APR-2020 08.23.59 +00    <null>                     <null>    28444494 


So you can see the values for each column.

How far back you can go is limited by your undo_retention and how many other changes happen on the database. If you want to guarantee history for a period of time, you can enable flashback data archive https://www.oracle.com/ocom/groups/public/@otn/documents/webcontent/4421812.pdf

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

David D. From Paris, April 21, 2020 - 11:10 am UTC

Hello,

If you want to obtain the list of updated columns for a given table, the solution I propose to you is very simple: use an UPDATE trigger with the UPDATING predicate.


=================================================== ===========================================
Definition of the trigger
=================================================== ===========================================
The first step is to define an AFTER UPDATE trigger on the table to be monitored. To identify the updated columns, we will use the IF UPDATING predicate.
The UPDATING predicate (a predicate is a function that returns TRUE or FALSE) has a particularity: it may or may not take a parameter. This parameter is the name of a column, in hard or in the form of a variable and it is Oracle which will say whether or not, this column has been updated. In this variable one positions alternately the name of all the columns of the table, recovered in DBA_TAB_COLUMNS.

The trigger code is as follows.
         create or replace trigger trig_update_col after update on HR.ZZ_TEST01
         DECLARED
           CURSOR c_cursor_column_name IS select column_name from dba_tab_columns where table_name = 'ZZ_TEST01' and owner = 'HR';
           v_name VARCHAR2 (50);
           v_result VARCHAR2 (2000);
           v_count NUMBER;
           v_date TIMESTAMP;

         begin
           v_name: = '';
           v_result: = '';
           v_count: = 0;

           SELECT to_char (SYSDATE, 'DD / MM / YYYY HH24: MI: SS') INTO v_date FROM dual;

           OPEN c_cursor_column_name;

           - retrieving the list of columns from the HR.ZZ_TEST01 table that we are monitoring.
           LOOP
             FETCH c_cursor_column_name INTO v_name;
             EXIT when c_cursor_column_name% NOTFOUND;

             if updating (v_name) then
               - If the column has been updated, then we build a character string listing the names of these columns.
               v_count: = v_count + 1;
               if v_count = 1 then
                 v_result: = v_name;
                        else
                 v_result: = v_result || ',' || v_name;
               end if;
                      end if;
           end loop;

           IF v_count> 0 THEN
             - Insert in the trace table containing a VARCHAR2 variable (4000 CHAR).
             insert into HR.ZZ_EVT values ​​('Columns updated on' || v_date || ':' || v_result || '.');
           END IF;

           CLOSE c_cursor_column_name;

           EXCEPTION
           WHEN OTHERS THEN
             dbms_output.put_line ('CODE ERROR' || TO_CHAR (SQLCODE) || '' || SQLERRM);
         end;

=================================================== ===========================================
Trigger tests
=================================================== ===========================================
The table ZZ_TEST01 contains three columns and we make an
update on one, two or three columns : OBJECT_NAME, OBJECT_TYPE and ID.
         update ZZ_TEST01 set OBJECT_NAME = 'TEST' || to_char (id), OBJECT_TYPE = 'TESTTYPE', id = 8888888 where id = 155;
         update ZZ_TEST01 set OBJECT_NAME = 'TEST' || to_char (id), OBJECT_TYPE = 'TESTTYPE' where id = 244;
         update ZZ_TEST01 set ID = 9999999 where id = 355;


The result meets our needs :-)
Of course, you can add the SQL command, the user who updated the table, etc., to the tracing table, but the principle does not change, use the predicate UPDATING.
         select * from ZZ_EVT;
         LIST_COLS
---------------------------------------------------------- ------------------------------------
          Columns updated on 04/29/17 4:50 p.m .: 55.000000000: OBJECT_NAME, OBJECT_TYPE, ID.
          Columns updated on 4/29/17 4:50 p.m .: 55.000000000: OBJECT_NAME, OBJECT_TYPE.
          Columns updated on 04/29/17 4:50 p.m .: 55.000000000: ID.

Connor McDonald
April 22, 2020 - 3:58 am UTC

Nice input.

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.