2-3 trucs (de plus) à propos de DBMS_REDEFINITION

Il y a bientôt un an, un post précédent intitulé 2 ou 3 trucs à propos de DBMS_REDEFINITION, illustrait avec un exemple simple et les questions adjacentes la réorganisation de tables en-lignes comme décrit dans la documentation Oracle 11g. Enfin, vous aurez souvent besoin de quelques informations supplémentaires lorsque vous travaillez avec ce package. Les raisons sont diverses ; (1) vous travaillez avec 9i et devez reconstruire les dépendances manuellement ; (2) Vous voulez simplement vérifier que tout se passe comme prévu ; (3) Les packages gênèrent des erreurs « normales » ou (4) n’importe quelle autre raison tordue.

Remarques :
* Validez que vous pouvez utiliser vos sauvegardes avant de faire ce qui suit ! Et, ce n’est pas pour rire !
* Ce qui suit devrait marcher en 9i, 10g et 11g ou tout du moins j’en ai test
é la majorité

Comment vérifier les propriétés de vos objets avec SQL*Plus ? C’est quelques-unes de ces questions qu’adresse ce nouveau post en proposant des requêtes simples que vous pourrez améliorer pour répondre à 80% des questions lorsque vous manipulez DBMS_REDEFINITION…

Positionner des variables:

Pour simplifier l’utilisation des requêtes, vous pouvez positionnez les valeurs du schéma et de la table sur laquelle vous allez appliquer la redéfinition comme dans le’exemple ci-dessous :

accept towner default 'SCOTT' -
prompt "What's the working schema [SCOTT] ? "

accept tname default 'EMP' -
prompt "What's the table [EMP] ? "

accept tinter default 'I$EMP' -
prompt "What's Interim table name you'll use [I$EMP] ? "

Premières info sur la table

Vous pouvez vérifier que la table existe que ce n’est pas un cluster ou une IOT. Si elle est partitionnée…

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'-------------------------------------------' OUTPUT,
'OWNER : '||OWNER OUTPUT,
'TABLE_NAME : '||TABLE_NAME OUTPUT,
'TABLESPACE_NAME : '||TABLESPACE_NAME OUTPUT,
'CLUSTER_NAME : '||CLUSTER_NAME OUTPUT,
'IOT_NAME : '||IOT_NAME OUTPUT,
'STATUS : '||STATUS OUTPUT,
'NUM_ROWS : '||NUM_ROWS OUTPUT,
'DEGREE : '||DEGREE OUTPUT,
'TABLE_LOCK : '||TABLE_LOCK OUTPUT,
'PARTITIONED : '||PARTITIONED OUTPUT,
'ROW_MOVEMENT : '||ROW_MOVEMENT OUTPUT,
'MONITORING : '||MONITORING OUTPUT,
'DEPENDENCIES : '||DEPENDENCIES OUTPUT,
'COMPRESSION : '||COMPRESSION OUTPUT,
'-------------------------------------------' OUTPUT
from dba_tables
where table_name='&&tname'
and owner='&&towner';

Vérifier que la table peut-être réorganisée en ligne

C’est globalement la première question. Pour cela, vous allez utiliser CAN_REDEF_TABLE soit avec sa clé primaire :

BEGIN
DBMS_REDEFINITION.CAN_REDEF_TABLE('&&towner','&&tname',
dbms_redefinition.cons_use_pk);
END;
/

Soit, s’il n’y a pas de clé primaire et que vous savez ce que vous faites avec son ROWID :

BEGIN
DBMS_REDEFINITION.CAN_REDEF_TABLE('&&towner','&&tname',
dbms_redefinition.cons_use_rowid);
END;
/

S’il n’y a pas d’erreur, vous pouvez continuer…

L’espace nécessaire

C’est bête à dire mais il faut mieux savoir si on s’attaque à 3 Mo ou 300 Go ! La requête ci-dessous affiche l’ensemble des segments de type « TABLE » et « INDEX » associés à votre table :

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

with seglist as (
select OWNER,TABLE_NAME
from dba_tables
where table_name='&&tname'
and owner='&&towner'
union all
select OWNER,INDEX_NAME
from dba_indexes
where table_name='&&tname'
and table_owner='&&towner')
select
'--SEGMENT # '||ROWNUM||' -----------------' OUTPUT,
'OWNER : '||OWNER OUTPUT,
'SEGMENT_NAME : '||SEGMENT_NAME OUTPUT,
'SEGMENT_TYPE : '||SEGMENT_TYPE OUTPUT,
'TABLESPACE_NAME : '||TABLESPACE_NAME OUTPUT,
'PARTITIONS/SUBPART : '||part# OUTPUT,
'MBYTES : '||round(BYTES/1024/1024,2) OUTPUT,
'-------------------------------------------' OUTPUT
from (
select owner, segment_name, segment_type, TABLESPACE_NAME,
sum(bytes) bytes, count(1) part#
from dba_segments
where (owner,segment_name)
in (select * from seglist)
group by owner, segment_name, segment_type, TABLESPACE_NAME);

Et celle qui suit liste les différents espaces disponibles pour stocker ces segments à condition que vous indiquez dans un premier temps une liste de tablespaces (Entre guillemets et séparés par des virgules) :

accept ts default '''USERS'',''DEMO''' -
prompt "Enter a quoted-comma separated tablespace list ['USERS','DEMO'] :"

col tablespace_name format a18
col file_name format a80
col ALLOC_MB format 9,999.9
col USED_MB format 9,999.9
col DELTA_MB format 9,999.9
set lines 120
set head on
set pages 100

prompt "Usable space within the datafiles..."

select f.tablespace_name ,
f.file_name ,
round(f.BYTES/1024/1024,1) ALLOC_MB,
round(nvl(sum(free.BYTES),0)/1024/1024,1) USED_MB ,
round((f.BYTES-nvl(sum(free.BYTES),0))/1024/1024,1) DELTA_MB
from dba_data_files f, dba_free_space free
where f.tablespace_name in (&&ts)
and f.tablespace_name=free.tablespace_name(+)
and f.file_id=free.file_id(+)
group by f.tablespace_name,f.file_id, f.file_name, f.BYTES
having (f.BYTES-nvl(sum(free.BYTES),0))/1024/1024 > 10
order by f.tablespace_name, DELTA_MB;

select tablespace_name,
file_name,
round(BYTES/1024/1024,1) ALLOC_MB,
round(USER_BYTES/1024/1024,1) USED_MB,
round((BYTES-USER_BYTES)/1024/1024,1) DELTA_MB
from dba_data_files
where tablespace_name in (&&ts)
and (BYTES-USER_BYTES)/1024/1024 > 10
order by tablespace_name, DELTA_MB;

prompt "List of files that can grow in size..."

col MAX_MB format 9,999.9
col SIZE_MB format 9,999.9
col DELTA_MB format 9,999.9
col INCR_B format 9,999.9

select tablespace_name,
file_name,
round(MAXBYTES/1024/1024,1) MAX_MB,
round(BYTES/1024/1024,1) SIZE_MB,
round((MAXBYTES-BYTES)/1024/1024,1) DELTA_MB,
round(increment_by,1) INCR_B
from dba_dat a_files
where tablespace_name in (&&ts)
and AUTOEXTENSIBLE='YES'
and MAXBYTES>BYTES
order by tablespace_name, DELTA_MB;

éventuellement, ajoutez des fichiers de données à vos tablespaces à l’aide de la commande alter tablespace … add datafile ….

Le LDD de la table

Une autre chose utile à récupérer est le DDL complet de la table puisque vous allez vous en servir pour construire la table intermédiaire. J’en ai déjà parlé, le package DBMS_METADATA est très utile pour l’utilisation de DBMS_REDEFINITION. Enfin attention, parce qu’il va falloir utiliser ce DDL avec précaution…

var v_sql clob

exec :v_sql:=dbms_metadata.get_ddl('TABLE',-
'&&tname',-
'&&towner');

set head on
set long 10000
set lines 180
set longchunksize 10000

print v_sql

Un bon conseil, c’est :

  • De renommer la table (Par exemple en la préfixant part I$)
  • De changer ce que vous voulez changer et en particulier,
    • Changer le tablespace (Surtout pour les grosses tables) pour le cas ou vous abandonneriez en cours, c’est plus simple ensuite de supprimer le tablespace et ça évite de créez des espace supplémentaires qu’on aura peut-être ensuite du mal à réclamer
    • Supprimer toutes les contraintes (en dehors de la clé primaire si vous utilisez cons_use_pk, ce qui est la méthode préférée) puisque la procédure copy_table_dependents va les recréer et changer leur nom
    • renommer la contrainte PK aussi avec la même notation (e.g. préfixe I$)
    • supprimer toutes les clause de stockage et utiliser cette du tablespace, sauf si vous avez une idée plus précise.
    • Intégrer vos propres changements

A partir de cette étapes, 2 visions s’affrontent ; celle de la 9i qui vous oblige à passer en revue et construire vous-même les dépendances (i.e. les index, les contraintes, les privilèges, statistiques…) et celles de la 10g et 11g qui le font automatiquement pour vous ! Quoiqu’il en soit, il est bon de savoir de quoi on parle non ?

Démarrer la redéfinition…

Maintenant que vous êtes prêt, attention aux performances ! Assurez-vous que la construction de la nouvelle table (insert as select) et la capture des transactions sur cette table (MV Log) sera supportée par votre base de données. Maintenant que vous êtes sur vous pourrez démarrer la redéfinition. Voici un exemple simple qui utilise les clés primaires et ne change pas les colonnes de la table :

BEGIN
DBMS_REDEFINITION.START_REDEF_TABLE('&&towner','&&tname','&&tinter',
null, dbms_redefinition.cons_use_pk);
END;

Si vous voulez utiliser la construction des objets dépendants ce qui est beaucoup plus simple si vous utilisez 10g ou 11g, je vous renvoie à la lecture de mon premier post au sujet de DBMS_REDEFINITION.

Maintenant, lister les index :

De la table originale

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Index #'||ROWNUM||'---------------------------' OUTPUT,
'OWNER : '||OWNER OUTPUT,
'INDEX_NAME : '||INDEX_NAME OUTPUT,
'INDEX_TYPE : '||INDEX_TYPE OUTPUT,
'TABLESPACE_NAME : '||TABLESPACE_NAME OUTPUT,
'UNIQUENESS : '||UNIQUENESS OUTPUT,
'COMPRESSION : '||COMPRESSION OUTPUT,
'LOGGING : '||LOGGING OUTPUT,
'STATUS : '||STATUS OUTPUT,
'NUM_ROWS : '||NUM_ROWS OUTPUT,
'LAST_ANALYZED : '||LAST_ANALYZED OUTPUT,
'PARTITIONED : '||PARTITIONED OUTPUT,
'-------------------------------------------' OUTPUT
from dba_indexes
where table_name='&&tname'
and table_owner='&&towner'
order by index_name;

set lines 120
set head on
col INDEX_OWNER format a10
col INDEX_NAME format a20
col COLUMN_POSITION format 99
col COLUMN_NAME format a20
col DESCEND format a4

select INDEX_OWNER,
INDEX_NAME,
COLUMN_POSITION,
COLUMN_NAME,
DESCEND
from dba_ind_columns
where TABLE_NAME='&&tname'
and TABLE_OWNER='&&towner'
order by INDEX_OWNER, INDEX_NAME, COLUMN_POSITION;

et, à titre de comparaison, la même chose pour la table intermédiaire :

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Index #'||ROWNUM||'---------------------------' OUTPUT,
'OWNER : '||OWNER OUTPUT,
'INDEX_NAME : '||INDEX_NAME OUTPUT,
'INDEX_TYPE : '||INDEX_TYPE OUTPUT,
'TABLESPACE_NAME : '||TABLESPACE_NAME OUTPUT,
'UNIQUENESS : '||UNIQUENESS OUTPUT,
'COMPRESSION : '||COMPRESSION OUTPUT,
'LOGGING : '||LOGGING OUTPUT,
'STATUS : '||STATUS OUTPUT,
'NUM_ROWS : '||NUM_ROWS OUTPUT,
'LAST_ANALYZED : '||LAST_ANALYZED OUTPUT,
'PARTITIONED : '||PARTITIONED OUTPUT,
'-------------------------------------------' OUTPUT
from dba_indexes
where table_name='&&tinter'
and table_owner='&&towner'
order by index_name;

set lines 120
set head on
col INDEX_OWNER format a10
col INDEX_NAME format a20
col COLUMN_POSITION format 99
col COLUMN_NAME format a20
col DESCEND format a4

select INDEX_OWNER,
INDEX_NAME,
COLUMN_POSITION,
COLUMN_NAME,
DESCEND
from dba_ind_columns
where TABLE_NAME='&&tinter'
and TABLE_OWNER='&&towner'
order by INDEX_OWNER, INDEX_NAME, COLUMN_POSITION;

Notez bien que si vous utilisez la méthode manuelle, vous aurez envie de renommer ces index à l’issue de l’opération avec la commande « ALTER INDEX schema.index RENAME TO … » et ceci afin de laissez l’environnement tel qu’il été avant votre passage.

Attention aux propriétés de l’index comme unique ou bitmap; l’effet peut-être atroce.

Ensuite les contraintes

Sur la table d’origine :

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Constraint # '||ROWNUM||' -----------------------' OUTPUT,
'CONSTRAINT_NAME : '||CONSTRAINT_NAME OUTPUT,
'CONSTRAINT_TYPE : '||CONSTRAINT_TYPE OUTPUT,
'R_OWNER : '||R_OWNER OUTPUT,
'R_CONSTRAINT_NAME : '||R_CONSTRAINT_NAME OUTPUT,
'DELETE_RULE : '||DELETE_RULE OUTPUT,
'STATUS : '||STATUS OUTPUT,
'DEFERRABLE : '||DEFERRABLE OUTPUT,
'DEFERRED : '||DEFERRED OUTPUT,
'VALIDATED : '||VALIDATED OUTPUT,
'GENERATED : '||GENERATED OUTPUT,
'BAD : '||BAD OUTPUT,
'RELY : '||RELY OUTPUT,
'LAST_CHANGE : '||LAST_CHANGE OUTPUT,
'INDEX_OWNER : '||INDEX_OWNER OUTPUT,
'INDEX_NAME : '||INDEX_NAME OUTPUT,
'INVALID : '||INVALID OUTPUT,
'VIEW_RELATED : '||VIEW_RELATED OUTPUT,
'-------------------------------------------' OUTPUT
from dba_constraints
where table_name='&&tname'
and owner='&&towner'
order by constraint_name;

Que vous pouvez comparer à celle de la table intermédiaire :

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Constraint # '||ROWNUM||' -----------------------' OUTPUT,
'CONSTRAINT_NAME : '||CONSTRAINT_NAME OUTPUT,
'CONSTRAINT_TYPE : '||CONSTRAINT_TYPE OUTPUT,
'R_OWNER : '||R_OWNER OUTPUT,
'R_CONSTRAINT_NAME : '||R_CONSTRAINT_NAME OUTPUT,
'DELETE_RULE : '||DELETE_RULE OUTPUT,
'STATUS : '||STATUS OUTPUT,
'DEFERRABLE : '||DEFERRABLE OUTPUT,
'DEFERRED : '||DEFERRED OUTPUT,
'VALIDATED : '||VALIDATED OUTPUT,
'GENERATED : '||GENERATED OUTPUT,
'BAD : '||BAD OUTPUT,
'RELY : '||RELY OUTPUT,
'LAST_CHANGE : '||LAST_CHANGE OUTPUT,
'INDEX_OWNER : '||INDEX_OWNER OUTPUT,
'INDEX_NAME : '||INDEX_NAME OUTPUT,
'INVALID : '||INVALID OUTPUT,
'VIEW_RELATED : '||VIEW_RELATED OUTPUT,
'-------------------------------------------' OUTPUT
from dba_constraints
where table_name='&&tinter'
and owner='&&towner'
order by constraint_name;

Notez bien que si vous utilisez la méthode manuelle, vous aurez envie de renommer ces contraintes à l’issue de l’opération avec la commande « ALTER TABLE schema.table RENAME CONSTRAINT aa TO bb » et ceci afin de laissez l’environnement tel qu’il été avant votre passage.

Si vous utilisez la méthode automatique veillez à enregistrer les contraintes que vous avez créés (y compris la PK ou les NOT NULL). Utilisez la procedure register_dependent_object.

Vous voudrez également valider les contraintes une fois celle-ci activées (**ATTENTION** n’activez pas vos contraintes vous même ! Et en tout cas, jamais aucune Foreign Key). En effet DBMS_REDEFINITION pour des raisons évidentes de temps de traitement ne le fait pas. Il est important que VALIDATED ou NOT VALIDATED peut entrainer des changements de plans et les propriétés des contraintes comme deferable ou not validated peuvent avoir un effet atroce.

Puis les clés étrangères qui référencent votre table

Au même titre que les clés entrangère de votre table, les clés étrangères qui la référence doivent être créées avant la fin de la redêfinition. Elles seront créées désactivée et activée automatiquement à la fin de la procedure. Voici un select qui affiche ces contraintes :

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Constraint # '||ROWNUM||' -----------------------' OUTPUT,
'CONSTRAINT_NAME : '||a.CONSTRAINT_NAME OUTPUT,
'CONSTRAINT_TYPE : '||a.CONSTRAINT_TYPE OUTPUT,
'R_OWNER : '||a.R_OWNER OUTPUT,
'R_CONSTRAINT_NAME : '||a.R_CONSTRAINT_NAME OUTPUT,
'DELETE_RULE : '||a.DELETE_RULE OUTPUT,
'STATUS : '||a.STATUS OUTPUT,
'DEFERRABLE : '||a.DEFERRABLE OUTPUT,
'DEFERRED : '||a.DEFERRED OUTPUT,
'VALIDATED : '||a.VALIDATED OUTPUT,
'GENERATED : '||a.GENERATED OUTPUT,
'BAD : '||a.BAD OUTPUT,
'RELY : '||a.RELY OUTPUT,
'LAST_CHANGE : '||a.LAST_CHANGE OUTPUT,
'INDEX_OWNER : '||a.INDEX_OWNER OUTPUT,
'INDEX_NAME : '||a.INDEX_NAME OUTPUT,
'INVALID : '||a.INVALID OUTPUT,
'VIEW_RELATED : '||a.VIEW_RELATED OUTPUT,
'-------------------------------------------' OUTPUT
from dba_constraints a, dba_constraints b
where b.table_name='&&tname'
and b.owner='&&towner'
and b.owner=a.r_owner
and b.r_constraint_name=a.constraint_name
order by a.constraint_name;

Vous pouvez afficher de même les contraintes qui référencent la table intermédiaire. Vous devriez en avoir un nombre identique sauf transformation volontaire :

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Constraint # '||ROWNUM||' -----------------------' OUTPUT,
'CONSTRAINT_NAME : '||a.CONSTRAINT_NAME OUTPUT,
'CONSTRAINT_TYPE : '||a.CONSTRAINT_TYPE OUTPUT,
'R_OWNER : '||a.R_OWNER OUTPUT,
'R_CONSTRAINT_NAME : '||a.R_CONSTRAINT_NAME OUTPUT,
'DELETE_RULE : '||a.DELETE_RULE OUTPUT,
'STATUS : '||a.STATUS OUTPUT,
'DEFERRABLE : '||a.DEFERRABLE OUTPUT,
'DEFERRED : '||a.DEFERRED OUTPUT,
'VALIDATED : '||a.VALIDATED OUTPUT,
'GENERATED : '||a.GENERATED OUTPUT,
'BAD : '||a.BAD OUTPUT,
'RELY : '||a.RELY OUTPUT,
'LAST_CHANGE : '||a.LAST_CHANGE OUTPUT,
'INDEX_OWNER : '||a.INDEX_OWNER OUTPUT,
'INDEX_NAME : '||a.INDEX_NAME OUTPUT,
'INVALID : '||a.INVALID OUTPUT,
'VIEW_RELATED : '||a.VIEW_RELATED OUTPUT,
'---------------------------------------------' OUTPUT
from dba_constraints a, dba_constraints b
where b.table_name='&&tinter'
and b.owner='&&towner'
and b.owner=a.r_owner
and b.r_constraint_name=a.constraint_name
order by a.constraint_name;

A la fin de l’opération, vous voudrez :

Et encore… les triggers et (11g uniquement) les journaux de vues matérialisées.

Dans l’ensemble c’est assez rare, toutefois, vous voudrez au moins vérifier que vos tables originales n’ont pas de trigger ou de journaux vues matérialisées avec les commandes ci-dessous.

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Trigger # '||ROWNUM||-----------------------' OUTPUT,
'OWNER : '||OWNER OUTPUT,
'TRIGGER_NAME : '||TRIGGER_NAME OUTPUT,
'TRIGGERING_EVENT : '||TRIGGERING_EVENT OUTPUT,
'WHEN_CLAUSE : '||WHEN_CLAUSE OUTPUT,
'STATUS : '||STATUS OUTPUT,
'-------------------------------------------' OUTPUT
from dba_triggers
where table_name='&&tname'
and table_owner='&&towner'
order by trigger_name;

select
'--MVIEW LOGS # '||ROWNUM||' -----------------' OUTPUT,
'LOG_TABLE : '||LOG_TABLE OUTPUT,
'LOG_TRIGGER : '||LOG_TRIGGER OUTPUT,
'-------------------------------------------' OUTPUT
from dba_mview_logs
where master='&&tname'
and log_owner='&&towner'
order by log_table;

Pour gérer ces cas, retournez (encore !) à la documentation. Le principe reste le même; créez les objets « disabled » et finish_redef_table les activera

Il y a aussi les privilèges sur la table :

Chaque détail compte ! Et celui la peut vous couter cher si vos utilisateurs n’ont plus accès à votre table. Vérifiez sur la table d’origine :

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Object Privileges # '||ROWNUM||' --------' OUTPUT,
'GRANTEE : '||GRANTEE OUTPUT,
'PRIVILEGE : '||PRIVILEGE OUTPUT,
'GRANTOR : '||GRANTOR OUTPUT,
'GRANTABLE : '||GRANTABLE OUTPUT,
'HIERARCHY : '||HIERARCHY OUTPUT,
'-------------------------------------------' OUTPUT
from dba_tab_privs
where table_name='&&tname'
and owner='&&towner'
order by grantee, privilege;

Et sur la table intermédiaire :

set head off
set lines 120
set pages 100
col output format a120
col output fold_a

select
'--Object Privileges # '||ROWNUM||' --------' OUTPUT,
'GRANTEE : '||GRANTEE OUTPUT,
'PRIVILEGE : '||PRIVILEGE OUTPUT,
'GRANTOR : '||GRANTOR OUTPUT,
'GRANTABLE : '||GRANTABLE OUTPUT,
'HIERARCHY : '||HIERARCHY OUTPUT,
'-------------------------------------------' OUTPUT
from dba_tab_privs
where table_name='&&tinter'
and owner='&&towner'
order by grantee, privilege;

Notez que théoriquement, vous pouvez avoir des privilèges sur des colonnes. Je vous engage à les vérifiez aussi ! Notez aussi que ce type de requête peut vous aider si c’est encore nécessaire :

set lines 120

select 'grant '||PRIVILEGE||' on &&towner..&&tinter to '||GRANTEE||';'
from dba_tab_privs
where table_name='&&tname'
and owner='&&towner';

Ensuite (Ce n’est pas encore terminé ?) – Il faut vérifier les statistiques

Sur la table et les index d’origine ! Mais surtout assurez-vous que les histogrammes sont aussi les même sur la table d’origine :

set head on
col COLUMN_NAME format a25

select column_name, count(*)
from dba_tab_histograms
where TABLE_NAME='&&tname'
and OWNER='&&towner'
group by column_name
order by column_name;

Et sur la table intermédiaire :

set head on
col COLUMN_NAME format a25

select column_name, count(*)
from dba_tab_histograms
where TABLE_NAME='&&tinter'
and OWNER='&&towner'
group by column_name
order by column_name;

Si vous utilisez la méthode automatique, ces valeurs doivent être gérées. Enfin, ma confiance est aveugle parce que je n’ai pas vérifié à quelle étape c’est fait. Si c’est manuel, vous pouvez, idéalement utiliser un export/transformation/import de statistiques (Ne vous manquez pas !) ou plus simplement utiliser la même méthode de collecte en vous assurant des histogrammes.

Synchronisez vos objets…

Rien de sorcier. Les ordres LMD appliqués depuis le démarrage de la redéfinition peuvent etre appliqués a votre table intermédiaire a l’aide de la commande ci-dessous :

BEGIN
DBMS_REDEFINITION.SYNC_INTERIM_TABLE('&&towner','&&tname','&&tinter');
END;
/

Vous pouvez basculer ? NON !!! Attendez

En plus des plans de requêtes qui peuvent changer (à cause de stats, de constraintes non validées ou du fait de la reorganisation), DBMS_REDEFINITION va invalider un certain nombre d’objets qui font référence à votre table avec un impact sur les programmes qui les utilisent. C’est un domaine où 11g apporte par rapport à ses ainées. Enfin, ceux qui suivent mes autres conversations en anglais savent de quoi je parle (cf ce post). Ca peut-être une bonne idée de vérifiez de quels objets il s’agit avant de foncer.

Ça commence à faire beaucoup alors j’ai écrit encore un autre post ici pour expliquer comment en 5 minutes afficher les dépendances avec votre table.

Voila c’est fini…

Lancez la commande magique qui termine tout :

BEGIN
DBMS_REDEFINITION.SYNC_INTERIM_TABLE('&&towner','&&tname','&&tinter');
END;
/

Enfin n’oubliez pas :

  • de supprimer l’ensemble des contraintes de la table ainsi que les clés étrangères qui référencent désormais la table intérimaire
  • de supprimer la table intérimaire
  • de valider les contraintes
  • de renommer les index, les contraintes ou les triggers si ce n’est pas déjà fait

Et si jamais il vous arrive une catastrophe, souvenez-vous de ce conseil : « Testez toujours vos programmes sur un environnement représentatif »… Oops trop tard !

PS: Si vous avez d’autres idées de requêtes sur ce sujet, elle sont les bien venues. Merci de
laisser vos commentaires