DATAPUMP exportiert falschen DEFAULT Tablespace bei partitionierten Segmenten
Mittels Oracle Datapump wurde ein Export – expdp – einer Datenbank durchgeführt. Beim Import – impdp – des Dumps konnten einige Segmente (Tabellen und Indizes) aufgrund fehlender Tablespaces nicht angelegt werden. Die vom Datapump vermeintlich fehlenden Tablespaces waren jedoch noch nicht einmal auf dem Quellsystem vorhanden, was mich schon sehr verwunderte. Eine Gemeinsamkeit hatten alle Segmente – sie waren partitioniert.
Wo lag nun der Fehler? Beim Datapump Export oder beim Import oder war gar das DataDictionary des Quellsystems korrupt? Nach einigen Stunden Suche hatte ich auf das eigentliche Problem entdeckt.
Beim Anlegen eines partitionierten Segmentes wurde ein Default Tablespace angegeben, welcher im Laufe der Zeit umbenannt oder gelöscht wurde. Im DataDictionary findet keine automatische Umbenennung bzw. Korrektur statt.
Problemdarstellung
Das folgende Demoscript stellt das eigentliche Problem detailiert dar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
-- 1. Tablespaces anlegen CREATE TABLESPACE tblsp_default DATAFILE '/ora01/oradata/meineDB/tblsp_default.dfb' SIZE 128k AUTOEXTEND ON NEXT 128k MAXSIZE UNLIMITED DEFAULT STORAGE( INITIAL 50k NEXT 50k PCTINCREASE 0); CREATE TABLESPACE tblsp_jahr2013 DATAFILE '/ora01/oradata/meineDB/tblsp_jahr2013.dfb' SIZE 128k AUTOEXTEND ON NEXT 128k MAXSIZE UNLIMITED DEFAULT STORAGE( INITIAL 50k NEXT 50k PCTINCREASE 0); CREATE TABLESPACE tblsp_jahr2014 DATAFILE '/ora01/oradata/meineDB/tblsp_jahr2014.dfb' SIZE 128k AUTOEXTEND ON NEXT 128k MAXSIZE UNLIMITED DEFAULT STORAGE( INITIAL 50k NEXT 50k PCTINCREASE 0); CREATE TABLESPACE tblsp_jahr_maxvalue DATAFILE '/ora01/oradata/meineDB/tblsp_jahr_maxvalue.dfb' SIZE 128k AUTOEXTEND ON NEXT 128k MAXSIZE UNLIMITED DEFAULT STORAGE( INITIAL 50k NEXT 50k PCTINCREASE 0); -- 2. Partitionierte Tabelle anlegen und einen Default Tablespace - tblsp_default - vergeben. CREATE TABLE test_part ( ID NUMBER , JAHR INTEGER) TABLESPACE tblsp_default PARTITION BY RANGE (JAHR) ( PARTITION JAHR_2013 VALUES LESS THAN ( 2014 ) TABLESPACE tblsp_jahr2013, PARTITION JAHR_2014 VALUES LESS THAN ( 2015 ) TABLESPACE tblsp_jahr2014, PARTITION JAHR_MAXVALUE VALUES LESS THAN ( MAXVALUE ) TABLESPACE tblsp_jahr_maxvalue ); -- 3. Selektion des DDL Scriptes SELECT dbms_metadata.get_ddl( 'TABLE', 'TEST_PART', USER ) DDL FROM dual; -- OUTPUT DLL CREATE TABLE "BESSERDICH"."TEST_PART" ( "ID" NUMBER, "JAHR" NUMBER(*,0) ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE( BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "TBLSP_DEFAULT" .... -- 4. Default Tablespace wird gelöscht DROP TABLESPACE tblsp_default INCLUDING CONTENTS AND DATAFILES; |
Schritt 3 – DDL Generierung – nochmals ausführen und man sieht, dass der Default Tablespace sich nicht geändert hat. Das hat zur Folge, dass die Import der Tabelle nicht funktioniert hätte.
Folgende SQL Befehle löschen die Segmente und Tablespaces des Demoscriptes aus der DB.
1 2 3 4 5 |
DROP TABLE test_part ; DROP TABLESPACE tblsp_default INCLUDING CONTENTS AND DATAFILES; DROP TABLESPACE tblsp_jahr2013 INCLUDING CONTENTS AND DATAFILES; DROP TABLESPACE tblsp_jahr2014 INCLUDING CONTENTS AND DATAFILES; DROP TABLESPACE tblsp_jahr_maxvalue INCLUDING CONTENTS AND DATAFILES; |
Lösungsansatz
Für mich stellt sich die eigentliche Frage, ist das ein Bug oder Feature? M.E. darf ein Tablespace nicht gelöscht werden, wenn dieser in irgend einer Art und Weise von einem Segment referenziert wird – in diesem Fall der Default Tablespace. Zumindest sollte die Datenbank den Default Tablespace der Tabelle auf den Default Tablespace des Users ändern.
Um das Problem im Quellsystem zu beheben, habe ich folgendes Script entwickelt, welches alle Default Tablespaces ermittelt, die es in Wirklichkeit gar nicht mehr gibt. Diese werden dann auf den Default Tablespace des USERs geändert. Somit ist sichergestellt, dass bei einem Export mittels Datapump ein erfolgreicher Import durchgeführt werden kann.
1 2 3 4 5 6 7 8 9 |
SELECT 'ALTER TABLE ' || upt.table_name || ' MODIFY DEFAULT ATTRIBUTES TABLESPACE ' || (Select default_tablespace from user_users) || ';' DDL FROM user_part_tables upt WHERE 1=1 AND upt.def_tablespace_name NOT IN (SELECT dt.tablespace_name FROM dba_tablespaces dt) UNION SELECT 'ALTER INDEX ' || upi.index_name || ' MODIFY DEFAULT ATTRIBUTES TABLESPACE ' || (SELECT default_tablespace FROM user_users) || ';' DDL FROM user_part_indexes upi WHERE 1=1 AND upi.def_tablespace_name NOT IN (SELECT tablespace_name from dba_tablespaces); |
SCHREIBEN SIE EINEN KOMMENTAR