Canalblog
Editer l'article Suivre ce blog Administration + Créer mon blog
Publicité
Blog d'un DBA sur le SGBD Oracle et SQL
30 juin 2021

LogMiner 3 : infos utiles pour les ordres DML et DDL - LogMiner 3: useful informations for DML and DDL orders

 

Introduction
Dans cet article nous allons voir quelles sont les infos intéressantes dans LogMiner pour les opérations DML et DDL.

Autres articles sur LogMiner : 

          - LogMiner 1 : initialisation et démarrage; pas si simple que ça
          - LogMiner 2 : les archived redologs, le mode archive log, la FRA
          - LogMiner 4 : impossible de récupérer le contenu d'une table droppée par erreur

 



Points d'attention
N/A.
 



Base de tests
Une base Oracle 19c multi-tenants.




Exemples
============================================================================================
Base de tests
============================================================================================
On modifie le format de NLS_DATE_FORMAT pour afficher l'heure. La base est en mode Archive.
     SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
     Session altered.

     SQL> archive log list
     Database log mode Archive log Mode
     Automatic archival Enabled
     Archive destination USE_DB_RECOVERY_FILE_DEST
     Oldest online log sequence 42
     Next log sequence to archive 44
     Current log sequence 44

Je suis connecté à ma PDB applicative, orcl, comme user HR et je crée une table de test.

     SQL> show user con_name
     USER is "HR"
     CON_NAME
     ------------------------------
     ORCL

     SQL> create table ZZ1 (ID NUMBER(8) PRIMARY KEY, NAME VARCHAR2(50 CHAR) NOT NULL, FIRST_NAME VARCHAR2(50 CHAR) NOT NULL);
     Table created.


============================================================================================
Insérer, modifier, supprimer des données et exécuter un ordre DDL
============================================================================================

L'étape concernant les opérations DML et DDL se fera sous la forme de quatre transactions.


Transaction 1 : plusieurs opérations DML avec COMMIT

INSERT N rows en une opération
     SQL> insert into ZZ1 (SELECT LEVEL, 'DUPONT', 'DAVID' FROM dual CONNECT BY LEVEL <= 10);
     10 rows created.

INSERT 1 row

     SQL> insert into ZZ1 values(11, 'DURAND', 'PIERRE');
     1 row created.

UPDATE N rows en une opération

     SQL> update ZZ1 set FIRST_NAME = 'JEAN' where ID > 6;
     5 rows updated.

UPDATE 1 row

     SQL> update ZZ1 set NAME = 'ARINAGE' where ID = 1;
     1 row updated.

DELETE N rows en une opération

     SQL> delete from ZZ1 where ID in (3, 4, 5);
     3 rows deleted.

DELETE 1 row

     SQL> delete from ZZ1 where ID = 11;
     1 row deleted.

On récupère maintenant l'id de la transaction en cours. Cela permettra d'identifier facilement les opérations dans LogMiner; pensez à activer l'affichage à l'écran des programmes PL/SQL via le paramètre serveroutput. Ensuite on valide la transaction.

     SQL> set serveroutput on
     SQL> exec dbms_output.put_line(dbms_transaction.local_transaction_id);
     16.5.515
     PL/SQL procedure successfully completed.

     SQL> COMMIT;
     Commit complete.


Transaction 2 : une seule opération DML avec COMMIT

DELETE 1 row
     SQL> delete from ZZ1 where ID = 2;
     1 row deleted.

     SQL> exec dbms_output.put_line(dbms_transaction.local_transaction_id);

     18.15.503
     PL/SQL procedure successfully completed.

     SQL> COMMIT;

     Commit complete.


Transaction 3 : une seule opération DML avec ROLLBACK

UPDATE 1 row
     SQL> update ZZ1 set NAME = 'DUBOIS' where ID = 8;
     1 row updated.

     SQL> exec dbms_output.put_line(dbms_transaction.local_transaction_id);

     14.2.381
     PL/SQL procedure successfully completed.

     SQL> ROLLBACK;

     Rollback complete.


Transaction 4 : une opération DDL avec COMMIT automatique
A noter qu'il est impossible de récupérer facilement l'id de la transaction puisqu'elle est créée et fermée via l'exécution de l'ordre DDL; via un trigger système ce serait peut-être possible mais c'est hors sujet.

     SQL> exec dbms_output.put_line( dbms_transaction.local_transaction_id );
     PL/SQL procedure successfully completed.

     SQL> create table ZZ2 (ID2 NUMBER(8) PRIMARY KEY, NAME2 VARCHAR2(50 CHAR) NOT NULL, FIRST_NAME2 VARCHAR2(50 CHAR) NOT NULL);

     Table created.

     SQL> exec dbms_output.put_line( dbms_transaction.local_transaction_id );

     PL/SQL procedure successfully completed.


============================================================================================
Activation de LogMiner avec les redologs archivés
============================================================================================

Nous sommes le 24 juin et les derniers redologs archivés n'ont que les opérations du 11 juin au plus tard. Pour plus de lisibilité, je ne mets qu'une fois le chemin des archived redologs : /u01/app/oracle/fast_recovery_area/ORCLCDB/archivelog.

     SQL> select sysdate from dual;
     SYSDATE
     --------------------
     24-JUN-2021 10:09:58

     SQL> select name, FIRST_TIME from v$archived_log order by 1;

     NAME FIRST_TIME
     --------------------------------------------------------------
     .../2021_06_10/o1_mf_1_38_jd3znbf5_.arc 08-JUN-2021 13:42:17
     .../2021_06_10/o1_mf_1_39_jd3zs9fc_.arc 10-JUN-2021 07:59:06
     .../2021_06_10/o1_mf_1_40_jd3zskow_.arc 10-JUN-2021 08:01:45
     .../2021_06_11/o1_mf_1_41_jd6tfgx9_.arc 10-JUN-2021 08:01:53
     .../2021_06_11/o1_mf_1_42_jd6tfjlt_.arc 11-JUN-2021 09:48:30
     .../2021_06_11/o1_mf_1_43_jd6tfmps_.arc 11-JUN-2021 09:48:32

Je me connecte au CDB$ROOT car il est impossible de faire un switch logfile depuis une PDB pour archiver les redologs. Plutôt que d'utiliser SQL*Plus, j'utilise SQLcl (commande sqlui), qui permet de mieux gérer la longueur d'affichage des colonnes d'un SELECT.

     [oracle@localhost ~]$ sqlui SYS@orclcdb as SYSDBA
     SQLcl: Release 19.1 Production on Thu Jun 24 10:11:19 2021
     ...

     SQL> show user con_name
     USER is "SYS"
     CON_NAME
     ------------------------------
     CDB$ROOT

     SQL> alter system switch logfile;

     System altered.
     SQL> alter system switch logfile;
     System altered.
     SQL> alter system switch logfile;
     System altered.

Les archived redologs qui m'intéressent sont les trois derniers, ceux avec les opérations du jour, donc du 24 Juin.

     SQL> select name, FIRST_TIME, NEXT_TIME from v$archived_log order by 1;
     NAME FIRST_TIME NEXT_TIME
     -----------------------------------------------------------------------------------
     .../2021_06_10/o1_mf_1_38_jd3znbf5_.arc 08-JUN-2021 13:42:17 10-JUN-2021 07:59:06
     .../2021_06_10/o1_mf_1_39_jd3zs9fc_.arc 10-JUN-2021 07:59:06 10-JUN-2021 08:01:45
     .../2021_06_10/o1_mf_1_40_jd3zskow_.arc 10-JUN-2021 08:01:45 10-JUN-2021 08:01:53
     .../2021_06_11/o1_mf_1_41_jd6tfgx9_.arc 10-JUN-2021 08:01:53 11-JUN-2021 09:48:30
     .../2021_06_11/o1_mf_1_42_jd6tfjlt_.arc 11-JUN-2021 09:48:30 11-JUN-2021 09:48:32
     .../2021_06_11/o1_mf_1_43_jd6tfmps_.arc 11-JUN-2021 09:48:32 11-JUN-2021 09:48:35
     .../2021_06_24/o1_mf_1_44_jf9593s1_.arc 11-JUN-2021 09:48:35 24-JUN-2021 10:22:27
     .../2021_06_24/o1_mf_1_45_jf9595nm_.arc 24-JUN-2021 10:22:27 24-JUN-2021 10:22:29
     .../2021_06_24/o1_mf_1_46_jf95979n_.arc 24-JUN-2021 10:22:29 24-JUN-2021 10:22:31
     9 rows selected.

On ouvre une session LogMiner avec ces redologs archivés.

     SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY, FOREIGN KEY, UNIQUE INDEX) COLUMNS;
     Database altered.

     SQL> SELECT SUPPLEMENTAL_LOG_DATA_MIN FROM V$DATABASE;

     SUPPLEMENTAL_LOG_DATA_MIN
     ____________________________
     IMPLICIT

Je mets uniquement dans la session logminer les redologs archivés contenant les opérations du 24 juin. Attention, pour le premier fichier, la procédure est NEW pour le paramètre OPTIONS; ADDFILE pour les autres.

     SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE( LOGFILENAME => '/u01/app/oracle/fast_recovery_area/ORCLCDB/archivelog/2021_06_24/o1_mf_1_44_jf9593s1_.arc', OPTIONS => DBMS_LOGMNR.NEW);
     PL/SQL procedure successfully completed.

     SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE( LOGFILENAME => '/u01/app/oracle/fast_recovery_area/ORCLCDB/archivelog/2021_06_24/o1_mf_1_45_jf9595nm_.arc', OPTIONS => DBMS_LOGMNR.ADDFILE);
     PL/SQL procedure successfully completed.

     
SQL> EXECUTE DBMS_LOGMNR.ADD_LOGFILE( LOGFILENAME => '/u01/app/oracle/fast_recovery_area/ORCLCDB/archivelog/2021_06_24/o1_mf_1_46_jf95979n_.arc', OPTIONS => DBMS_LOGMNR.ADDFILE);

     PL/SQL procedure successfully completed.

     
SQL> EXECUTE DBMS_LOGMNR.START_LOGMNR(OPTIONS => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);

     PL/SQL procedure successfully completed.

Le SELECT dans V$LOGMNR_CONTENTS ne renvoie pas d'erreur, c'est bon, LogMiner est démarré.

     SQL> select count(*) from V$LOGMNR_CONTENTS;
     COUNT(*)
     ___________
     90608


============================================================================================
LogMiner : les informations importantes
============================================================================================

La vue V$LOGMNR_CONTENTS
Voici les colonnes de V$LOGMNR_CONTENTS qui me semblent les plus intéressantes pour auditer les opérations DML et DDL.

     SQL> desc v$logmnr_contents
     Name Null? Type
     ----------------------------------
     SCN NUMBER
     ...
     TIMESTAMP DATE
     OPERATION VARCHAR2(32)
     ROLLBACK NUMBER
     SEG_OWNER VARCHAR2(386)
     SEG_NAME VARCHAR2(256)
     TABLE_NAME VARCHAR2(386)
     SEG_TYPE_NAME VARCHAR2(32)
     TABLE_SPACE VARCHAR2(92)
     ROW_ID VARCHAR2(18)
     USERNAME VARCHAR2(384)
     OS_USERNAME VARCHAR2(4000)
     MACHINE_NAME VARCHAR2(4000)
     SESSION# NUMBER
     SESSION_INFO VARCHAR2(4000)
     SQL_REDO VARCHAR2(4000)
     SQL_UNDO VARCHAR2(4000)
     INFO VARCHAR2(64)
     STATUS NUMBER
     SRC_CON_NAME VARCHAR2(384)
     ...
     CON_ID NUMBER

Les colonnes pour identifier la transaction, avec l'id renvoyé par dbms_transaction.local_transaction_id, sont les suivantes :

          - XIDUSN Transaction ID undo segment number of the transaction that generated the change
          - XIDSLT Transaction ID slot number of the transaction that generated the change
          - XIDSQN Transaction ID sequence number of the transaction that generated the change
          - XID Raw representation of the transaction identifier


Voici les infos des opérations effectuées sur la table ZZ1 du user HR depuis le 22 juin.
Notez que pour les opérations DML, la colonne Operation contient l'ordre DML précis: INSERT, UPDATE ou DELETE. Mais, pour les opérations DDL, nous avons juste le libellé DDL, pas le type d'opération comme CREATE, ALTER... Autre point important : la colonne SQL_REDO ne contient pas toujours syntaxiquement parlant le même ordre que celui exécuté mais, d'un point de vue des données, il est identique.


Fichier texte généré par le SELECT
Mon ordre SQL pour interroger V$LOGMNR_CONTENTS sur les opérations survenues sur HR.ZZ1 est le suivant.
     SQL> select XIDUSN || '-' || XIDSLT || '-' || XIDSQN AS "Trans ID", TIMESTAMP, USERNAME, OPERATION, ROW_ID, SQL_REDO, SQL_UNDO from V$LOGMNR_CONTENTS where SEG_OWNER = 'HR' and SEG_NAME = 'ZZ1' order by TIMESTAMP, "Trans ID";
Voici un lien vers le fichier texte du résultat du SELECT; sa lecture est plus facile que via une page web : LogMiner_Select



Transaction 1 : plusieurs opérations DML validées
La transaction est la 16-5-515 (voir ci-dessus).
On ne constate aucune différence dans les ordres SQL pour rejouer ou annuler un INSERT multiple ou un INSERT d'un seul enregistrement; idem pour les UPDATEs et DELETEs. Notez que pour rejouer un INSERT (colonne SQL_REDO), Oracle n'utilise pas de rowid (normal) et propose le même ordre INSERT que celui que j'ai exécuté, alors que c'est différent pour les UPDATEs et DELETEs.

INSERT N row : l'ordre était SQL> insert into ZZ1 (SELECT LEVEL, 'DUPONT', 'DAVID' FROM dual CONNECT BY LEVEL <= 10);
Pour annuler l'INSERT (colonne SQL_UNDO), Oracle traduit cela par un DELETE en ajoutant la colonne ROWID à la clause WHERE. La question est : pourquoi insérer dans la clause WHERE du DELETE les colonnes ID, NAME, FIRST_NAME alors que le ROWID est suffisant pour identifier une ligne? mystère! A moins que, suite à un shrink de la table ou un export/import datapump, le ROWID de la colonne SQL_UNDO pointe désormais vers un autre enregistrement? Alors, avec ces trois colonnes en plus, Oracle ne pourra pas updater le mauvais enregistrement car les valeurs de ces trois colonnes ne correspondront pas.

     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ____________ _______________________ ___________ ____________ 
     
16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAH insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('8','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '8' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAH';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAG insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('7','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '7' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAG';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAF insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('6','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '6' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAF';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAE insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('5','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '5' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAE';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAD insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('4','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '4' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAD';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAC insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('3','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '3' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAC';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAB insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('2','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '2' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAB';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAA insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('1','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '1' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAA';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAI insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('9','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '9' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAI';


     16-5-515 24-JUN-2021 09:56:11 HR INSERT AAATmwAAMAAAMc3AAJ insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('10','DUPONT','DAVID'); delete from "HR"."ZZ1" where "ID" = '10' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAJ';


INSERT 1 row : l'ordre était insert into ZZ1 values(11, 'DURAND', 'PIERRE');
     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ____________ _______________________ ___________ ____________ 
     
16-5-515 24-JUN-2021 09:56:33 HR INSERT AAATmwAAMAAAMc3AAK insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values
('11','DURAND','PIERRE'); delete from "HR"."ZZ1" where "ID" = '11' and "NAME" = 'DURAND' and "FIRST_NAME" = 'PIERRE' and ROWID = 'AAATmwAAMAAAMc3AAK';



UPDATE N rows : l'ordre était update ZZ1 set FIRST_NAME = 'JEAN' where ID > 6;
Pour rejouer un UPDATE ou pour l'annuler avec un autre UPDATE, Oracle utilise dans les deux cas le ROWID dans la clause WHERE mais, et là aussi c'est étonnant, il utilise le contenu de la colonne FIRST_NAME alors que je ne l'ai pas utilisée dans mon UPDATE. Plus bizarre, pourquoi est-ce que la colonne NAME n'est-elle pas utilisée alors que FIRST_NAME l'est? Je pense que c'est parce que la colonne mise à jour est FIRST_NAME et pas NAME; plus bas, dans la transaction 14-2-381, nous verrons la même chose mais avec la colonne NAME cette fois.

     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ____________ _______________________ ___________ ____________ 
     
16-5-515 24-JUN-2021 09:56:41 HR UPDATE AAATmwAAMAAAMc3AAG update "HR"."ZZ1" set "FIRST_NAME" = 'JEAN' where "ID" = '7' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAG'; update "HR"."ZZ1" set "FIRST_NAME" = 'DAVID' where "ID" = '7' and "FIRST_NAME" = 'JEAN' and ROWID = 'AAATmwAAMAAAMc3AAG';


     16-5-515 24-JUN-2021 09:56:41 HR UPDATE AAATmwAAMAAAMc3AAH update "HR"."ZZ1" set "FIRST_NAME" = 'JEAN' where "ID" = '8' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAH'; update "HR"."ZZ1" set "FIRST_NAME" = 'DAVID' where "ID" = '8' and "FIRST_NAME" = 'JEAN' and ROWID = 'AAATmwAAMAAAMc3AAH';


     16-5-515 24-JUN-2021 09:56:41 HR UPDATE AAATmwAAMAAAMc3AAI update "HR"."ZZ1" set "FIRST_NAME" = 'JEAN' where "ID" = '9' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAI'; update "HR"."ZZ1" set "FIRST_NAME" = 'DAVID' where "ID" = '9' and "FIRST_NAME" = 'JEAN' and ROWID = 'AAATmwAAMAAAMc3AAI';


     16-5-515 24-JUN-2021 09:56:41 HR UPDATE AAATmwAAMAAAMc3AAJ update "HR"."ZZ1" set "FIRST_NAME" = 'JEAN' where "ID" = '10' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAJ'; update "HR"."ZZ1" set "FIRST_NAME" = 'DAVID' where "ID" = '10' and "FIRST_NAME" = 'JEAN' and ROWID = 'AAATmwAAMAAAMc3AAJ';


     16-5-515 24-JUN-2021 09:56:41 HR UPDATE AAATmwAAMAAAMc3AAK update "HR"."ZZ1" set "FIRST_NAME" = 'JEAN' where "ID" = '11' and "FIRST_NAME" = 'PIERRE' and ROWID = 'AAATmwAAMAAAMc3AAK'; update "HR"."ZZ1" set "FIRST_NAME" = 'PIERRE' where "ID" = '11' and "FIRST_NAME" = 'JEAN' and ROWID = 'AAATmwAAMAAAMc3AAK';



UPDATE 1 row : l'ordre était update ZZ1 set NAME = 'ARINAGE' where ID = 1;

Dans la clause WHERE de mon UPDATE, j'utilise seulement la colonne ID, eh bien Oracle, pour rejouer ou annuler cet UPDATE, utilise les colonnes ID, NAME et ROWID...
     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ____________ _______________________ ___________ ____________ 
     
16-5-515 24-JUN-2021 09:57:15 HR UPDATE AAATmwAAMAAAMc3AAA update "HR"."ZZ1" set "NAME" = 'ARINAGE' where "ID" = '1' and "NAME" = 'DUPONT' and ROWID = 'AAATmwAAMAAAMc3AAA'; update "HR"."ZZ1" set "NAME" = 'DUPONT' where "ID" = '1' and "NAME" = 'ARINAGE' and ROWID = 'AAATmwAAMAAAMc3AAA';



DELETE N rows : l'ordre était delete from ZZ1 where ID in (3, 4, 5);

     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ____________ _______________________ ___________ ____________ 
     
16-5-515 24-JUN-2021 09:57:28 HR DELETE AAATmwAAMAAAMc3AAC delete from "HR"."ZZ1" where "ID" = '3' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAC'; insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('3','DUPONT','DAVID');


     16-5-515 24-JUN-2021 09:57:28 HR DELETE AAATmwAAMAAAMc3AAD delete from "HR"."ZZ1" where "ID" = '4' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAD'; insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('4','DUPONT','DAVID');


     16-5-515 24-JUN-2021 09:57:28 HR DELETE AAATmwAAMAAAMc3AAE delete from "HR"."ZZ1" where "ID" = '5' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAE'; insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('5','DUPONT','DAVID');



DELETE 1 row : l'ordre était delete from ZZ1 where ID = 11;

     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ____________ _______________________ ___________ ____________ 
     
16-5-515 24-JUN-2021 09:57:41 HR DELETE AAATmwAAMAAAMc3AAK delete from "HR"."ZZ1" where "ID" = '11' and "NAME" = 'DURAND' and "FIRST_NAME" = 'JEAN' and ROWID = 'AAATmwAAMAAAMc3AAK'; insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('11','DURAND','JEAN');



Transaction 2 : une seule opération DML, validée

DELETE 1 row : l'ordre était delete from ZZ1 where ID = 2;
La transaction est maintenant la 18-15-503, le COMMIT de la transaction précédente 16-5-515 a donc créé une nouvelle transaction, avec un ID différent.
     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ____________ _______________________ ___________ ____________ 
     
18-15-503 24-JUN-2021 09:58:48 HR DELETE AAATmwAAMAAAMc3AAB delete from "HR"."ZZ1" where "ID" = '2' and "NAME" = 'DUPONT' and "FIRST_NAME" = 'DAVID' and ROWID = 'AAATmwAAMAAAMc3AAB'; insert into "HR"."ZZ1"("ID","NAME","FIRST_NAME") values ('2','DUPONT','DAVID');



Transaction 3 : une seule opération DML, annulée

UPDATE 1 row : l'ordre était update ZZ1 set NAME = 'DUBOIS' where ID = 8;
Dans LogMiner on voit l'équivalent de l'UPDATE saisi pour rejouer celui-ci, l'UPDATE pour l'annuler (colonne SQL_UNDO) mais aussi l'opération inverse générée par Oracle suite au ROLLBACK. A noter que mon UPDATE utilise la colonne ID dans la clause WHERE mais Oracle utilise le ROWID pour annuler la mise à jour. Détail amusant : l'UPDATE utilisé par Oracle lors du ROLLBACK n'est pas celui qui est dans la colonne SQL_UNDO du premier UPDATE, avec les colonnes ID, NAME et FIRST_NAME dans la clause WHERE; celui utilisé pour le ROLLBACK utilise juste le ROWID.
     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ____________ _______________________ ___________ ____________ 
     
14-2-381 24-JUN-2021 10:01:06 HR UPDATE AAATmwAAMAAAMc3AAH update "HR"."ZZ1" set "NAME" = 'DUBOIS' where "ID" = '8' and "NAME" = 'DUPONT' and ROWID = 'AAATmwAAMAAAMc3AAH'; update "HR"."ZZ1" set "NAME" = 'DUPONT' where "ID" = '8' and "NAME" = 'DUBOIS' and ROWID = 'AAATmwAAMAAAMc3AAH';


     14-2-381 24-JUN-2021 10:01:34 HR UPDATE AAATmwAAMAAAMc3AAH update "HR"."ZZ1" set "NAME" = 'DUPONT' where ROWID = 'AAATmwAAMAAAMc3AAH';

     31 rows selected.


Transaction 4 : une seule opération DDL, validée par auto-commit

Opération DDL : L'ordre était create table ZZ2 (ID2 NUMBER(8) PRIMARY KEY, NAME2 VARCHAR2(50 CHAR) NOT NULL, FIRST_NAME2 VARCHAR2(50 CHAR) NOT NULL);
Pour identifier les ordres DDL exécutés par un user, et non pas ceux joués en interne par Oracle, il faut chercher les opérations avec DDL dans la colonne OPERATION; la colonne SQL_UNDO est vide pour ce type d'opération. On notera le CREATE INDEX sur la colonne ID2 qui est PRIMARY KEY. Pour mémoire, la contrainte PRIMARY KEY signifie que la colonne est NOT NULL et UNIQUE. Pourquoi ajouter la contrainte UNIQUE à l'index alors qu'elle existe déjà sur la colonne et permet donc de tester chaque valeur avant qu'elle ne soit insérée dans l'index?
     SQL> select XIDUSN || '-' || XIDSLT || '-' || XIDSQN AS "Trans ID", TIMESTAMP, USERNAME, OPERATION, ROW_ID, SQL_REDO, SQL_UNDO from V$LOGMNR_CONTENTS where OPERATION = 'DDL' and SEG_OWNER = 'HR' AND UPPER(SQL_REDO) LIKE 'CREATE%ZZ2%' order by TIMESTAMP, "Trans ID";
     Trans ID TIMESTAMP USERNAME OPERATION ROW_ID SQL_REDO SQL_UNDO
     ___________ _______________________ ___________ ____________ _____________________ 
     18-8-504 24-JUN-2021 10:02:01 HR DDL AAAAAAAAAAAAAAAAAB create table ZZ2 (ID2 NUMBER(8) PRIMARY KEY, NAME2 VARCHAR2(50 CHAR) NOT NULL, FIRST_NAME2 VARCHAR2(50 CHAR) NOT NULL);

     18-8-504 24-JUN-2021 10:02:01 HR DDL AAAAAAAAAAAAAAAAAB CREATE UNIQUE INDEX "HR"."SYS_C0012561" on "HR"."ZZ2"("ID2") NOPARALLEL;


Publicité
Publicité
Commentaires
Blog d'un DBA sur le SGBD Oracle et SQL
Publicité
Archives
Blog d'un DBA sur le SGBD Oracle et SQL
  • Blog d'un administrateur de bases de données Oracle sur le SGBD Oracle et sur les langages SQL et PL/SQL. Mon objectif est de vous faire découvrir des subtilités de ce logiciel, des astuces, voir même des surprises :-)
  • Accueil du blog
  • Créer un blog avec CanalBlog
Visiteurs
Depuis la création 340 939
Publicité