Sur Oracle version 11g:
Après Google, je ne trouve pas de moyen simple de récupérer de l'espace libre après avoir supprimé une table.
J'ai trouvé beaucoup d'explications, expliquant comment le fichier de données devient fragmenté, la grande pile de requêtes ennuyeuses que vous devez exécuter afin de déplacer "l'espace vide" à la fin du fichier de données (table par table ... même lorsque vous avoir 200 tables!?).
Ensuite, vous devez réduire la taille du fichier de données en "devinant" de combien vous pouvez la réduire, ou vous devez savoir exactement quelle est votre "taille de bloc" ... Et enfin, vous ne devez pas oublier de "reconstruire les index".
Voir par exemple: http://asktom.Oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:54178027703899
et http://www.Oracle-base.com/articles/misc/ReclaimingUnusedSpace.php
Existe-t-il une procédure PL/SQL simple qui, étant donné un nom d'espace de table ou un nom de fichier de données, le ferait pour ce travail? Ou tout autre outil Oracle similaire?
La réponse courte est Non. Malheureusement, la façon de procéder dans Oracle nécessite la "grande pile de requêtes ennuyeuses". Les articles auxquels vous avez lié sont parmi les meilleures informations disponibles sur le sujet. Le fichier de données devient en effet fragmenté, de sorte que même si l'espace libre existe en dessous du segment le plus élevé, Oracle ne le consolidera pas automatiquement quand un RESIZE
sera fait.
Pour "défragmenter" l'espace disque logique, vous devez déplacer ces segments au début du fichier de données plutôt qu'à la fin. Pour les tables, il s'agit d'un processus hors ligne, ce qui signifie que la table sera indisponible pendant le déplacement. Les index peuvent être déplacés hors ligne ou avec Enterprise Edition, ils peuvent être déplacés en ligne. Comme vous avez une fenêtre d'interruption, je vous recommande de suivre ces étapes.
A. Rétrécir les fichiers de données avec un espace libre au-delà de la ligne des hautes eaux. Cela peut être fait comme suit (la requête est similaire à la procédure de Frosty Z):
SELECT ceil( blocks*(a.BlockSize)/1024/1024) "Current Size",
ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 ) "Smallest Poss.",
ceil( blocks*(a.BlockSize)/1024/1024) -
ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 ) "Savings",
'alter database datafile '''|| file_name || ''' resize ' ||
ceil((nvl(hwm,1)*(a.BlockSize))/1024/1024/100)*100 || 'm;' "Command"
FROM (SELECT a.*, p.value BlockSize FROM dba_data_files a
JOIN v$parameter p ON p.Name='db_block_size') a
LEFT JOIN (SELECT file_id, max(block_id+blocks-1) hwm FROM dba_extents GROUP BY file_id ) b
ON a.file_id = b.file_id
WHERE ceil( blocks*(a.BlockSize)/1024/1024) - ceil( (nvl(hwm,1)*(a.BlockSize))/1024/1024 )
> 100 /* Minimum MB it must shrink by to be considered. */
ORDER BY "Savings" Desc;
B. Après avoir réduit les choses au-dessus de la ligne des hautes eaux, découvrez quels espaces de table bénéficieraient encore du déplacement des segments.
SELECT DISTINCT tablespace_name FROM
(
SELECT tablespace_name, block_id + blocks LastBlock,
lead(block_id) OVER (PARTITION BY File_ID
ORDER BY tablespace_name, file_id, block_id) NextBlock
FROM dba_free_space
) WHERE LastBlock <> NextBlock AND NextBlock IS NOT NULL;
C. Pour chacun de ces espaces de table, déterminez quels segments doivent être déplacés. (Remplacez USERS par le nom de votre espace de table ou joignez-le à la requête précédente)
SELECT distinct de.segment_name
FROM dba_extents de
JOIN
(
SELECT tablespace_name, file_id, MIN(block_id) LowestFreeBlock
FROM dba_free_space
WHERE tablespace_name = 'USERS'
GROUP BY tablespace_name, file_id
) dfs ON dfs.tablespace_name = de.tablespace_name AND dfs.file_id = de.file_id
WHERE de.tablespace_name = 'USERS'
AND de.block_id > dfs.LowestFreeBlock;
D. Déplacez chaque table et reconstruisez les index et les statistiques.
E. Répétez l'étape A.
Je viens de créer la plupart de ces requêtes, vous voudrez donc les tester soigneusement avant de les utiliser. Je suppose que vous pourriez créer une procédure qui utiliserait EXECUTE IMMEDIATE
pour créer les instructions réelles à exécuter dynamiquement, mais parce que les requêtes recevront ORA-08103: L'objet n'existe plus pendant le déplacement, je pense qu'il est préférable de contrôler ce processus manuellement même si cela signifie un peu plus de temps /effort.
Solution partielle inspirée de cette page :
Il ne réorganise pas l'espace libre mais détecte automatiquement l'espace libre disponible à la fin des fichiers de données et imprime les commandes "REDIMENSIONNER" appropriées.
DECLARE
BLKSIZE INTEGER;
BEGIN
SELECT VALUE INTO BLKSIZE FROM V$PARAMETER WHERE NAME = 'db_block_size';
FOR INDEX_ROW IN (
SELECT 'ALTER DATABASE DATAFILE ''' || FILE_NAME || ''' RESIZE ' || CEIL( (NVL(HWM,1)*BLKSIZE)/1024/1024 ) || 'M;' SHRINK_DATAFILES FROM DBA_DATA_FILES DBADF,
(SELECT FILE_ID, MAX(BLOCK_ID+BLOCKS-1) HWM FROM DBA_EXTENTS GROUP BY FILE_ID ) DBAFS
WHERE DBADF.FILE_ID = DBAFS.FILE_ID(+) AND CEIL(BLOCKS*BLKSIZE/1024/1024)- CEIL((NVL(HWM,1)* BLKSIZE)/1024/1024 ) > 0
) LOOP
DBMS_OUTPUT.PUT_LINE(INDEX_ROW.SHRINK_DATAFILES);
END LOOP;
END;
Après avoir surfé sur Google pendant des jours, j'ai trouvé l'exemple le plus simple et le plus clair pour récupérer l'espace libre dans l'espace de table après la suppression. J'espère que ça aide
Lien: http://www.dbforums.com/Oracle/976248-how-reduce-tablespaces-used-space-after-delete-records-2.html
solution:
ALTER TABLE MOVE demo
Créons un tableau avec 9999 lignes, chacune d'environ 1k:
SQL> create table t (x char(1000) default 'x' primary key);
Table created.
SQL> insert /*+ append nologging */ into t(x) select rownum from all_objects where rownum < 10000;
9999 rows created.
SQL> commit;
Commit complete.
Le tableau dispose de 29 extensions, pour un total de 14,6 millions:
SQL> select count(*), sum(bytes) from user_extents where segment_name='T';
COUNT(*) SUM(BYTES)
---------- ----------
29 14680064
Supprimons TOUTES les lignes:
SQL> delete from t;
9999 rows deleted.
SQL> commit;
Commit complete.
Maintenant - "surprise" - la table utilise toujours les mêmes extensions:
SQL> select count(*), sum(bytes) from user_extents where segment_name='T';
COUNT(*) SUM(BYTES)
---------- ----------
29 14680064
Pourquoi ? Parce que même si vous supprimez toutes les lignes du tableau, le High Water Mark n'est pas diminué - il n'est jamais diminué, pour permettre une concurrence maximale (Oracle est très sérieux quant à la maximisation de la concurrence, c'est-à-dire les performances et l'évolutivité; c'est la principale raison de son succès dans les applications d'entreprise).
La désallocation de l'espace inutilisé (= espace au-dessus du HWM) n'aide pas beaucoup (car il n'y a pas beaucoup d'espace inutilisé au-dessus du HWM):
SQL> alter table t deallocate unused;
Table altered.
SQL> select count(*), sum(bytes) from user_extents where segment_name='T';
COUNT(*) SUM(BYTES)
---------- ----------
29 13959168
Maintenant, MOVONS la table, ce qui signifie essentiellement cloner la table (y compris les déclencheurs, les contraintes, etc.), transférer les lignes, supprimer la "vieille" table et renommer la nouvelle - le tout fait par le noyau, donc super-sûr même en cas de panne machine/serveur:
SQL> alter table t move;
Table altered.
Maintenant, nous avons maintenant seulement l'étendue initiale allouée:
SQL> select count(*), sum(bytes) from user_extents where segment_name='T';
COUNT(*) SUM(BYTES)
---------- ----------
1 65536
Mise en garde: il arrive normalement que de nombreux/tous les index de la table soient inutilisables après le déplacement (pas dans ce cas, mais j'exécute 9.2.0.4, la dernière version, qui a probablement optimisé le processus en cas de tables totalement vides ):
SQL> col table_name form a30
SQL> col index_name form a30
SQL> set lines 123
SQL> select table_name, index_name, status from user_indexes where table_name='T';
TABLE_NAME INDEX_NAME STATUS
------------------------------ ------------------------------ ------------------------
T SYS_C002573 VALID
Si STATUS n'était pas VALIDE, vous pouvez simplement reconstruire manuellement les index:
SQL> alter index SYS_C002573 rebuild;
Index altered.
Ou vous pouvez automatiser l'ensemble du processus:
set serveroutput on size 100000
begin
for n in (select index_name from user_indexes where status <> 'VALID') loop
dbms_output.put_line ('rebuilding ' || n.index_name);
execute immediate 'alter index ' || n.index_name || ' rebuild';
end loop;
end;
/
Par exemple, définissons manuellement l'index sur INUTILISABLE:
SQL> alter index SYS_C002573 unusable;
Index altered.
SQL> set serveroutput on size 100000
SQL> begin
2 for n in (select index_name from user_indexes where status <> 'VALID') loop
3 dbms_output.put_line ('rebuilding ' || n.index_name);
4 execute immediate 'alter index ' || n.index_name || ' rebuild';
5 end loop;
6 end;
7 /
rebuilding SYS_C002573
PL/SQL procedure successfully completed.
HTH Alberto
Avant d'essayer de réduire du tout les fichiers de données, posez-vous la question: allez-vous créer à nouveau de nouveaux segments dans l'espace de table associé dans un avenir pas si lointain? Si oui, il ne sert à rien de rétrécir. L'espace sera simplement réutilisé pour vos nouveaux segments et vous vous épargnerez ainsi que le système beaucoup d'efforts en le laissant tel quel.
Comme indiqué précédemment, vous devrez déplacer toutes les 200+ tables de cet espace de table pour libérer de l'espace dans votre fichier de données, puis redimensionner pour récupérer l'espace. Mais au lieu d'exécuter toutes ces requêtes, 12c Enterprise Manager fait cette tâche. Vous devrez accéder à la base de données Accueil> Stockage> Espace disque logique. Sélectionnez l'espace disque logique sur lequel vous souhaitez travailler et cliquez sur Réorganiser. Il vous donnera une option pour afficher les instructions SQL qui sont sur le point d'être exécutées. Vous pouvez soit en prendre une copie et l'exécuter vous-même, soit planifier un travail dans EM.
Il crée en fait un autre espace de table, déplace tous les objets vers le nouvel espace de table, reconstruit les index et supprime les objets de l'ancien espace de table.
Il y a quelques inconvénients auxquels je peux penser. Cela devrait être fait pendant les heures creuses, sinon cela entraînera une erreur en disant que la ressource est occupée. Le fichier de données (pas l'espace de table) aura "reorg" ajouté à son nom, vers la fin.