Contournement erreur ora-08102 : clé d'index introuvable

Il y a quelques mois, j’ai rencontré une erreur « ORA-08102: clé d’index introuvable : obj# jjj, fichier kkk, bloc lll » dans une requête delete » sur une base Oracle 12.2.0.2.
A priori : il s’agit d’un index corrompu.
Cela peut être facilement corrigé en le recalculant.
Attention, il ne faut pas oublier le « online », sinon le problème peut subsister…

alter index monuser.monindex rebuild tablespace montablespace online;

Effectivement, tout refonctionne correctement.
Quelques jours plus tard : le problème réapparait sur plusieurs tables du même schéma.
En approfondissant un peu, je remarque, par la requête suivante,  que le nombre d’entrées dans la table et dans la pk est différent :

select (select /*+ NO_INDEX('matable') */ count(1) from monuser.matable), (select count(1) from monuser.matable) from dual';

Recherche dans « My Oracle Support » : ce problème apparait déjà en Oracle 7i.
Quelques correctifs sont testés mais le problème continue à réapparaitre régulièrement sur l’une ou l’autre table après les avoir appliqués.
Il est donc décidé de valider et corriger les index erronés après passage du traitement de création des lignes (donc avant le delete qui provoque l’erreur ORA-08102.
Pour cela, un petit script PL/SQL(toutes les tables concernées appartiennent au même schéma et leurs noms commencent de la meme façon) :

 set heading off
 set linesize 100
 set pagesize 0
 set term off
 set sqlprompt " "
 set FEEDBACK OFF
 set trimspool on
 WHENEVER OSERROR exit FAILURE
 WHENEVER SQLERROR EXIT SQL.SQLCODE
variable v_code_erreur Number;
 spool $wlogfile append
set serveroutput on;
declare
w_txt varchar2(500);
w_txt_2 varchar2(500);
w_rep varchar2(500);
w_rep1 number;
w_rep2 number;
w_index varchar2(30);
w_tablespace varchar2(30);
Cursor Curs_tables is
select owner, table_name from dba_tables where owner='${ORACLE_SCHEMA}' and table_name like '${ORACLE_TABLES}';
begin
 :v_code_erreur := 0;
select count(1) into w_rep1 from dba_users where username = '${ORACLE_SCHEMA}';
 if w_rep1 = 0 then
 w_rep := 'Schema $ORACLE_SCHEMA inconnu dans cette base';
 dbms_output.put_line(w_rep);
 :v_code_erreur := 1;
 else
select count(1) into w_rep1 from dba_tables where owner='${ORACLE_SCHEMA}' and table_name like '${ORACLE_TABLES}';
 if w_rep1 = 0 then
 w_rep := 'Aucune table correspondant au critere ${ORACLE_TABLES}';
 dbms_output.put_line(w_rep1);
 :v_code_erreur := 1;
 else;
For table_rec in Curs_tables loop
 w_txt := 'select (select /*+ NO_INDEX('||table_rec.table_name||') */ count(1) from '||table_rec.owner||'.'||table_rec.table_name||'), (select count(1) from '||table_rec.owner||'.'||table_rec.table_name||') from dual';
/* dbms_output.put_line(w_txt); */
 execute immediate w_txt into w_rep1, w_rep2;
 w_rep := table_rec.owner||'.'||table_rec.table_name||' - table : '||w_rep1||' - index : '||w_rep2;
 if w_rep1 <> w_rep2 then
 w_rep := w_rep||' ***** ERREUR *****';
 dbms_output.put_line(w_rep);
 select index_name into w_index from dba_constraints where owner=table_rec.owner and table_name=table_rec.table_name and constraint_type = 'P';
 select tablespace_name into w_tablespace from dba_indexes where owner = table_rec.owner and index_name = w_index;
 w_txt_2 := 'alter index '||table_rec.owner||'.'||w_index||' rebuild tablespace '||w_tablespace||' online';
 dbms_output.put_line(w_txt_2);
 execute immediate w_txt_2;
 execute immediate w_txt into w_rep1, w_rep2;
 w_rep := table_rec.owner||'.'||table_rec.table_name||' - table : '||w_rep1||' - index : '||w_rep2;
 if w_rep1 <> w_rep2 then
 w_rep := w_rep||' ***** TOUJOURS EN ERREUR';
 :v_code_erreur := 1;
 else
 w_rep := w_rep||' ***** CORRIGE *****';
 end if;
 end if;
 dbms_output.put_line(w_rep);
 end loop;
 end if;
 end if;
end;
/

Depuis, des index erronés apparaissent environs une fois pas semaine mais sont immédiatement corrigés avant la détection de l’erreur…