Réorganisation de tables partitionnées

Je partage via ce post un script PL/SQL permettant la réorganisation de tables sous Oracle 10g & 11g, via la méthode ALTER TABLE MOVE.
Il est très utile sur des bases de données utilisant le partitionnement car il supporte la réorganisation de table partitionnées.

Les indexes de la table sont également reconstruits, faute de quoi l’ordre ALTER TABLE MOVE les laisse dans un état UNUSABLE.
Si les indexes sont partitionnés, le script supporte également le rebuild de ces derniers, partition par partition.

Noter que la méthode ALTER TABLE MOVE est à faire en dehors des plages d’ouvertures applicatives car elle rend les indexes unusable le temps de leur rebuild.

Le script se trouve en fin d’article.
Voyons un cas de test avec une table visant à stocker les collaborateurs d’une société avec une partition par site géographique.

CREATE TABLE EMP
(emp_id NUMBER(5), 
emp_prenom VARCHAR2(30),
emp_nom VARCHAR2(30),
emp_site VARCHAR2(20),
CONSTRAINT PK_EMP PRIMARY KEY (emp_id))
PARTITION BY LIST(emp_site)
(
PARTITION EMP_PARIS VALUES('Paris'),
PARTITION EMP_NANTES VALUES ('Nantes'),
PARTITION EMP_LYON VALUES('Bordeaux'),
PARTITION EMP_LILLE VALUES('Brest'),
PARTITION EMP_AUTRES VALUES(DEFAULT)
);

Cette table stocke les entrées dans la partition correspondante à chaque ville sur la base du champ emp_site.
Si une requête sur la table utilise une clause « WHERE emp_site = « , alors seule la partition correspondant à cette ville sera lue.
Créons un index partitionné LOCAL sur cette table afin d’avoir à la fois une table partitionnée et un index partitionné.

create index EMP_IDX_NOMPRENOM on EMP (emp_nom,emp_prenom) LOCAL;

Insérons une dizaine de lignes dans la table :

insert into EMP values (1,'Christophe','Carpentier','Paris');
insert into EMP values (2,'Charlotte','Morvan','Paris');
insert into EMP values (3,'Robert','Ledoux','Nantes');
insert into EMP values (4,'Nathalie','Marie','Nantes');
insert into EMP values (5,'Aurore','Pasquier','Bordeaux');
insert into EMP values (6,'Olivier','Jean','Bordeaux');
insert into EMP values (7,'René','Laroche','Brest');
insert into EMP values (8,'Paul','Fournier','Brest');
insert into EMP values (9,'Rene','Dupont','Brest');
insert into EMP values (10,'Bernard','Maurice','Brest');

Vérifions la présence des objets partitionnés :

set line 300
col table_name for a30;
col index_name for a30;
col partition_name for a30;

select table_name,partition_name from user_tab_partitions;
select index_name,partition_name from user_ind_partitions;

Voici l’appel du script pour la réorganisation de cette table.
Il prend en paramètre le schéma et le nom de la table visée :

[oracle@orapri scripts]$ ./reorg_table.sh TESTSCHEMA EMP
[INFO] - Reorganisation de la table_partitionee EMP
[INFO] ----> Reorganisation de la partition de table EMP_AUTRES
[INFO] ----> Reorganisation de la partition de table EMP_LILLE
[INFO] ----> Reorganisation de la partition de table EMP_LYON
[INFO] ----> Reorganisation de la partition de table EMP_NANTES
[INFO] ----> Reorganisation de la partition de table EMP_PARIS
[INFO] - Rebuild de l'index PK_EMP
[INFO] - Rebuild de l'index partitionne EMP_IDX_NOMPRENOM
[INFO] ----> Rebuild de la partition d'index EMP_AUTRES
[INFO] ----> Rebuild de la partition d'index EMP_LILLE
[INFO] ----> Rebuild de la partition d'index EMP_LYON
[INFO] ----> Rebuild de la partition d'index EMP_NANTES
[INFO] ----> Rebuild de la partition d'index EMP_PARIS
Elapsed: 00:00:00.48

Voici les sources du script :

#!/bin/ksh

###################################################################
# Ce script reorganise la table en parametre et tous ses indexes  #
###################################################################
# + Supporte les tables partitionnees							  #
# + Supporte les indexes partitionnes							  #
###################################################################

#Rappel d'usage
if [ $# -lt 2 ]
then
	echo 'USAGE : ksh reorg_tables.ksh  '
exit 1
fi

schema=$1
table=$2
log=reorg_tables.log

sqlplus -s << ENDOFSQL
/ as sysdba

	-- Parametres SQLPLUS
	set lines 80;
	set feedback off;
	set serveroutput on;
	set timing on;
	spool ${log};
	
	-- Debut du traitement dynamique
	DECLARE
		
		-- Curseur contenant l'ensemble des tables a reorganiser dans ce lot 1
		CURSOR C_ALL_TABLES IS ( 	SELECT owner,table_name,partitioned 
									FROM dba_tables 
									WHERE owner = '${schema}'
									AND table_name = '${table}'
								);
		
	BEGIN
		
		-- Parcours de toutes les tables
		FOR all_tab IN C_ALL_TABLES LOOP
			
			-- Si la table n'est pas partitionnee
			IF all_tab.partitioned = 'NO' THEN
				
				-- Ligne ajoutee au journal
				DBMS_OUTPUT.PUT_LINE('[INFO] - Reorganisation de la table '||all_tab.table_name);
				
				-- Execution de la reorganisation
				EXECUTE IMMEDIATE ('ALTER TABLE "'||all_tab.owner||'"."'||all_tab.table_name||'" MOVE PARALLEL 64');
				
				-- Repositionnement du NOPARALLEL sur la table
				EXECUTE IMMEDIATE ('ALTER TABLE "'||all_tab.owner||'"."'||all_tab.table_name||'" NOPARALLEL');
				
				-- Reorganisation du ou des index(es) de la table
				DECLARE
					
					-- Curseur contenant les indexes de la table
					CURSOR C_ALL_INDEXES IS (	SELECT owner,index_name,partitioned
												FROM dba_indexes
												WHERE owner = all_tab.owner
												AND index_type = 'NORMAL'
												AND table_name = all_tab.table_name
												);
				BEGIN
					
					FOR all_idx IN C_ALL_INDEXES LOOP
						
						-- Si l'index n'est pas partitionne
						IF all_idx.partitioned = 'NO' THEN
							
							-- Ligne ajoutee au journal
							DBMS_OUTPUT.PUT_LINE('[INFO] - Rebuild de l''index '||all_idx.index_name);
							
							-- Execution du rebuild de l'index
							EXECUTE IMMEDIATE('ALTER INDEX "'||all_idx.owner||'"."'||all_idx.index_name||'" REBUILD PARALLEL 64');
							
							-- Repositionnement du NOPARALLEL sur l'index
							EXECUTE IMMEDIATE('ALTER INDEX "'||all_idx.owner||'"."'||all_idx.index_name||'" NOPARALLEL');
							
						-- Si l'index est partitionne
						ELSE
							
							DECLARE 
								
								-- Curseur contenant les partitions de l'index
								CURSOR C_IDX_PARTITIONS IS (	SELECT partition_name
																FROM dba_ind_partitions
																WHERE index_name = all_idx.index_name);
								
							BEGIN
								
							DBMS_OUTPUT.PUT_LINE('[INFO] - Rebuild de l''index partitionne '||all_idx.index_name);

							-- Parcours des partitions d'indexes
							FOR idx_partitions IN C_IDX_PARTITIONS LOOP
								
								-- Ligne ajoutee au journal
								DBMS_OUTPUT.PUT_LINE('[INFO] ----> Rebuild de la partition d''index '||idx_partitions.partition_name);
								
								-- Execution du rebuild
								EXECUTE IMMEDIATE('ALTER INDEX "'||all_idx.owner||'"."'||all_idx.index_name||'" REBUILD PARTITION "'||idx_partitions.partition_name||'" PARALLEL 64'); 
								
							END LOOP;

							-- Repositionnement du NOPARALLEL sur l'index
							EXECUTE IMMEDIATE('ALTER INDEX "'||all_idx.owner||'"."'||all_idx.index_name||'" NOPARALLEL'); 

							END;
							
						END IF;
						
					END LOOP;
					
				END;
				
			-- Si la table est partitionee
			ELSE
				
				-- Reorganisation de la table partitionnee
				DECLARE
					
					-- Curseur contenant les partitions de la table
					CURSOR C_ALL_TABLE_PARTS IS (	SELECT partition_name
					FROM dba_tab_partitions
					WHERE table_name = all_tab.table_name
					AND table_owner = all_tab.owner
				);
					
				BEGIN
					
					-- Ligne ajoutee au journal
					DBMS_OUTPUT.PUT_LINE('[INFO] - Reorganisation de la table_partitionee '||all_tab.table_name);
					
					-- Parcours des partitions de la table
					FOR all_tab_partition IN C_ALL_TABLE_PARTS LOOP
						
						-- Ligne ajoutee au journal
						DBMS_OUTPUT.PUT_LINE('[INFO] ----> Reorganisation de la partition de table '||all_tab_partition.partition_name);
						
						-- Execution de la reorg de la partition
						EXECUTE IMMEDIATE('ALTER TABLE "'||all_tab.owner||'"."'||all_tab.table_name||'" MOVE PARTITION "'||all_tab_partition.partition_name||'"');
						
					END LOOP;
					
					-- Repositionnement du NOPARALLEL sur la table
					EXECUTE IMMEDIATE('ALTER TABLE "'||all_tab.owner||'"."'||all_tab.table_name||'" NOPARALLEL');
					
				END;
				
				-- Reorganisation du ou des index(es) de la table
				DECLARE
					
					-- Curseur contenant les indexes de la table
					CURSOR C_ALL_INDEXES IS (	SELECT owner,index_name,partitioned
												FROM dba_indexes
												WHERE owner = all_tab.owner
												AND index_type = 'NORMAL'
												AND table_name = all_tab.table_name
											);
				BEGIN
					
					FOR all_idx IN C_ALL_INDEXES LOOP
						
						-- Si l'index n'est pas partitionne
						IF all_idx.partitioned = 'NO' THEN
							
							-- Ligne ajoutee au journal
							DBMS_OUTPUT.PUT_LINE('[INFO] - Rebuild de l''index '||all_idx.index_name);
							
							-- Execution du rebuild de l'index
							EXECUTE IMMEDIATE('ALTER INDEX "'||all_idx.owner||'"."'||all_idx.index_name||'" REBUILD PARALLEL 64');
							
							-- Repositionnement du NOPARALLEL sur l'index
							EXECUTE IMMEDIATE('ALTER INDEX "'||all_idx.owner||'"."'||all_idx.index_name||'" NOPARALLEL');
							
						-- Si l'index est partitionne
						ELSE
							
							DECLARE 
								
								-- Curseur contenant les partitions de l'index
								CURSOR C_IDX_PARTITIONS IS (	SELECT partition_name
																FROM dba_ind_partitions
																WHERE index_name = all_idx.index_name
															);
							BEGIN

								DBMS_OUTPUT.PUT_LINE('[INFO] - Rebuild de l''index partitionne '||all_idx.index_name);

								-- Parcours des partitions d'indexes
								FOR idx_partitions IN C_IDX_PARTITIONS LOOP
									
									-- Ligne ajoutee au journal
									DBMS_OUTPUT.PUT_LINE('[INFO] ----> Rebuild de la partition d''index '||idx_partitions.partition_name);
									
									-- Execution du rebuild
									EXECUTE IMMEDIATE('ALTER INDEX "'||all_idx.owner||'"."'||all_idx.index_name||'" REBUILD PARTITION "'||idx_partitions.partition_name||'" PARALLEL 64'); 
									
								END LOOP;

								-- Repositionnement du NOPARALLEL sur l'index
								EXECUTE IMMEDIATE('ALTER INDEX "'||all_idx.owner||'"."'||all_idx.index_name||'" NOPARALLEL'); 

							END;
							
						END IF;
						
					END LOOP;
					
				END;
				
			END IF;
			
		END LOOP;	
		
	END;
	/
	
ENDOFSQL

exit 0