Introduction
Si vous voulez obtenir la liste des colonnes mises à jour en SQL pour une table donnée, à des fins d'audit, vous faites comment? Vous essayez de récupérer les ordres SQL UPDATE et vous décomposez celui-ci pour récupérer les noms des colonnes? Attention, certains ordres seront plus difficiles à traiter que d'autres comme la gestion des sous requêtes, des hints qui ne doivent pas être pris en compte etc etc. Vous utilisez un trigger et utilisez les paramètres OLD et NEW? Vous utilisez le package DBMS_FGA?

La solution que je vous propose est très simple : avec un trigger UPDATE et le prédicat UPDATING, c'est Oracle qui va nous dire quelles colonnes ont été mises à jour.



Points d'attention
Prévoir une liste de requêtes UPDATE suffisamment diverses pour bien valider votre code.



Base de tests
N'importe quelle base Oracle.



Exemples
============================================================================================
Définition du trigger
============================================================================================
La première étape consiste à définir un trigger AFTER UPDATE sur la table à surveiller. Pour identifier les colonnes mises à jour, nous utiliserons le prédicat IF UPDATING. A une époque, il n'était possible de créer un trigger que sur un évènement et un seul : INSERT, DELETE, UPDATE. Depuis quelques versions d'Oracle, il est possible de définir un trigger sur ces trois évènements ou sur un mix des trois. Bien sur le code sera exécuté indistinctement de l'évènement déclenché; c'est pour cela qu'Oracle a créé trois prédicat INSERTING, DELETING, UPDATING pour associer une partie de code à un évènement bien précis.

Mais le prédicat UPDATING (un prédicat est une fonction qui retourne VRAI ou FAUX) a une particularité : il peut prendre ou non un paramètre. Ce paramètre est le nom d'une colonne, en dur ou sous forme de variable et c'est Oracle qui dira si, oui ou non, cette colonne a été updatée. Dans cette variable on positionne alternativement le nom de toutes les colonnes de la table, récupérés dans DBA_TAB_COLUMNS.


Le code du trigger est le suivant.
         create or replace trigger trig_update_col after update on HR.ZZ_TEST01
         DECLARE
           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;

           -- récupération de la liste des colonnes de la table HR.ZZ_TEST01 qu'on surveille.
           LOOP
             FETCH c_cursor_column_name INTO v_name;
             EXIT when c_cursor_column_name%NOTFOUND;

             if updating(v_name) then
               -- Si la colonne a été updatée, alors on construit une chaîne de caractères listant le nom de ces colonnes.
               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 dans la table de traces contenant une variable VARCHAR2 (4000 CHAR).
             insert into HR.ZZ_EVT values('Colonnes mises à jour le ' || v_date || ' : ' || v_result || '.');
           END IF;

           CLOSE c_cursor_column_name;

           EXCEPTION
           WHEN OTHERS THEN
             dbms_output.put_line('ERREUR de code ' || TO_CHAR(SQLCODE) || ' ' || SQLERRM);  
         end;

============================================================================================
Tests du trigger
============================================================================================

La table ZZ_TEST01 contient trois colonnes et nous faisons un update sur une, deux ou trois colonnes.
         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;

Le résultat est conforme à notre besoin :-)
Bien sur on peut rajouter dans la table de traçage l'ordre SQL, le user qui a mis à jour la table etc etc mais le principe ne change pas, utiliser le prédicat UPDATING.
         select * from ZZ_EVT;
         LISTE_COLS
         ------------------------------------------------------------------------------------------------------------------------------------------------
         Colonnes mises à jour le 29/04/17 16:50:55,000000000 : OBJECT_NAME, OBJECT_TYPE, ID.
         Colonnes mises à jour le 29/04/17 16:50:55,000000000 : OBJECT_NAME, OBJECT_TYPE.
         Colonnes mises à jour le 29/04/17 16:50:55,000000000 : ID.