DOAG Datenbank Kolumne: Schema-Import auf Schleichwegen

  • Erstellt von Elke Fritsch
  • Datenbank Kolumne, Oracle, Datenbank

Die Oracle-Built-in-Packages erleichtern auch dem DBA die Arbeit, aber viele verwenden sie gar nicht.

Ich hatte häufiger das Problem, dass ich in unserer Produktionsumgebung einen Schema-Dump ziehen wollte, mich aber nicht auf dem Datenbank-Server anmelden konnte, weil gerade getestet wurde. Da die Operator im allgemeinen nicht gerade erfreut waren, wenn plötzlich neue Kommandozeilenfenster auf ihrem Display auftauchten, bastelte ich mir einfach eine Prozedur für den Datapump-Import übers Netzwerk. Diese Möglichkeit gibt es schon seit "ewigen" Zeiten (seit Version 10g).

Der Import benötigt zwar durchaus Ressourcen, aber die Tests wurden dadurch nicht spürbar langsamer und ersparten mir und den Kollegen in Asien Downtimes für Wartungsfenster. 

Der Code:

CREATE OR REPLACE PROCEDURE prc_schema_import_net (

     schema_in            VARCHAR2 DEFAULT user, -- Schemaname

     schema_new_in    VARCHAR2 DEFAULT NULL, -- Zielschema

     parallel_in            VARCHAR2 DEFAULT 1,

     directory_in          VARCHAR2 DEFAULT 'DATA_PUMP_DIR', -- für das Logfile

     db_link_in             VARCHAR2)

IS

    l_job_name      VARCHAR2(100);

    l_idx                 NUMBER;

    l_handle            NUMBER;

    l_percent_done   NUMBER := 0;

    l_job_state         VARCHAR2(30) := 'n.a.';

 

    -- nested Tables für das Monitoring des Import-Jobs

    logentry_nt      KU$_LOGENTRY; 

    jobstatus_nt     KU$_JOBSTATUS; 

    jobdesc_nt       KU$_JOBDESC;   

    status_nt         KU$_STATUS;   

 

BEGIN

   l_job_name := RTRIM(TO_CHAR(sysdate, 

        'yyyymmdd-hh24mi')||'_imp_'||schema_in||'_'||schema_new_in, '_');

   

    -- einen neuen Datapump-Job erstellen

   l_handle := DBMS_DATAPUMP.OPEN  (

                 operation => 'IMPORT',

                  job_mode => 'SCHEMA',

                  job_name => l_job_name,

               remote_link => db_link_in);

 

    -- Optionale Parallelisierung

   IF NVL(parallel_in,1) > 1 THEN

       DBMS_DATAPUMP.SET_PARALLEL(l_handle, parallel_in);

    END IF;

 

   -- Abschnitt für das Logfile (dafür braucht's ein Directory

   -- auch wenn der Dump selbst nicht gespeichert werden muss)

   DBMS_DATAPUMP.ADD_FILE (

           handle => l_handle,

         filename => l_job_name||'.log',

        directory => UPPER(directory_in),

         filetype => DBMS_DATAPUMP.ku$_file_type_log_file);

 

    -- Export aller Objekte des Schemas zum selben Zeitpunkt

   DBMS_DATAPUMP.SET_PARAMETER (

              handle => l_handle,

                name => 'FLASHBACK_TIME',

               value => 'systimestamp');

 

    -- Schemanamen übergeben

   DBMS_DATAPUMP.METADATA_FILTER (

                handle => l_handle,

                  name => 'SCHEMA_EXPR',

                 value => '='''||UPPER(schema_in)||'''');

 

   -- Beispiel für Objekte, die weggelassen werden sollen

   DBMS_DATAPUMP.METADATA_FILTER (

                handle => l_handle,

                  name => 'EXCLUDE_PATH_EXPR',

                 value => 'IN (''GRANT'',''STATISTICS'',''DB_LINK'')');

 

    -- falls gewünscht, Schemanamen beim Import ändern

    -- das Zielschema muss bereits existieren

   IF schema_new_in IS NOT NULL THEN

         DBMS_DATAPUMP.METADATA_REMAP (

               handle => l_handle,

                 name => 'REMAP_SCHEMA',

            old_value => UPPER(schema_in),

                value => UPPER(schema_new_in));

    END IF;

 

   -- Job starten

   DBMS_DATAPUMP.START_JOB  (handle => l_handle);

 

    -- Monitoring

    WHILE l_job_state NOT IN ('COMPLETED', 'STOPPED') LOOP

        DBMS_DATAPUMP.GET_STATUS(

                handle => l_handle,

                  mask => DBMS_DATAPUMP.ku$_status_job_error +

                         DBMS_DATAPUMP.ku$_status_job_status +

                         DBMS_DATAPUMP.ku$_status_wip,

               timeout => -1,

             job_state => l_job_state,

                status => status_nt);

 

        jobStatus_nt := status_nt.job_status;

 

        -- Der Fortschritt wird über percent_done angezeigt

        IF jobstatus_nt.percent_done != l_percent_done THEN

          DBMS_OUTPUT.PUT_LINE('Abgeschlossen %: ' ||

                               jobstatus_nt.percent_done);

          l_percent_done := jobstatus_nt.percent_done;

        END IF;

 

        -- Sammlung von work-in-progress und Fehlermeldungen für den Job

        IF BITAND(status_nt.mask, DBMS_DATAPUMP.ku$_status_wip) != 0 THEN

            logentry_nt := status_nt.wip;

        ELSE

            IF BITAND(status_nt.mask,

                  DBMS_DATAPUMP.ku$_status_job_error) != 0 THEN

                logentry_nt := status_nt.error;

            ELSE

                logentry_nt := null;

            END IF;

        END IF;

       

        -- wenn Fehlermeldungen aufgetaucht sind, durch die nested Table

        -- navigieren und die Fehler anzeigen

        IF logentry_nt IS NOT NULL THEN

            l_idx := logentry_nt.FIRST;

 

            WHILE l_idx IS NOT NULL LOOP

               DBMS_OUTPUT.PUT_LINE(logentry_nt(l_idx).LogText);

                l_idx := logentry_nt.NEXT(l_idx);

            END LOOP;

 

        END IF;

    END LOOP;

 

    DBMS_OUTPUT.PUT_LINE('Job abgeschlossen, Status: ' || l_job_state);

    DBMS_DATAPUMP.DETACH  (handle => l_handle);

 

EXCEPTION

    WHEN OTHERS THEN

 

   -- Wenn die Schritt OPEN und ATTACH nicht geklappt haben,

   -- dann ist der Jobhandle NULL

   DBMS_DATAPUMP.GET_STATUS(

        handle => NULL,

          mask => 8,

       timeout => 0,

     job_state => l_job_state,

        status => status_nt);

   logentry_nt := status_nt.error;

    l_idx := logentry_nt.FIRST;

 

    LOOP

      EXIT WHEN l_idx IS NULL;

      DBMS_OUTPUT.PUT_LINE('logLineNumber: ' ||

                            logentry_nt(l_idx).logLineNumber);

      DBMS_OUTPUT.PUT_LINE('errorNumber: ' || logentry_nt(l_idx).errorNumber);

      DBMS_OUTPUT.PUT_LINE('LogText: ' || logentry_nt(l_idx).LogText);

      l_idx := logentry_nt.NEXT(l_idx);

    END LOOP;

 

    DBMS_DATAPUMP.STOP_JOB(l_handle);

    RAISE;

END prc_schema_import_net;

 

Beispiel:

Als User system auf der Zieldatenbank

set serveroutput on

BEGIN

    prc_schema_import_net (

       schema_in => 'prod',

    directory_in => 'extern',

      db_link_in => 'prod_remote');

END;

 

Ausgabe z.B.

Master-Tabelle "SYSTEM"."20260420-2124_imp_prod" erfolgreich geladen/entladen

"SYSTEM"."20260420-2124_imp_prod":   wird gestartet

Objekttyp SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA wird verarbeitet

Objekttyp SCHEMA_EXPORT/DB_LINK wird verarbeitet

Objekttyp SCHEMA_EXPORT/SEQUENCE/SEQUENCE wird verarbeitet

Objekttyp SCHEMA_EXPORT/TABLE/TABLE wird verarbeitet

Objekttyp SCHEMA_EXPORT/TABLE/TABLE_DATA wird verarbeitet

Abgeschlossen %: 48

. . "PROD"."RESULTS"                            270.1 MB 5350208 Zeilen importiert

Abgeschlossen %: 90

. . "PROD"."TRACE_LOG"                        40.86 MB 102257 Zeilen importiert

Abgeschlossen %: 96

. . "PROD"."DEVICETEST"                       12.47 MB 206042 Zeilen importiert

Abgeschlossen %: 97

. . "PROD"."DEVICES"                              6.472 MB  106365 Zeilen importiert

Abgeschlossen %: 98

....

Objekttyp SCHEMA_EXPORT/PACKAGE/PACKAGE_SPEC wird verarbeitet

Objekttyp SCHEMA_EXPORT/PROCEDURE/PROCEDURE wird verarbeitet

Objekttyp SCHEMA_EXPORT/PACKAGE/COMPILE_PACKAGE/PACKAGE_SPEC/ALTER_PACKAGE_SPEC wird verarbeitet

Objekttyp SCHEMA_EXPORT/PROCEDURE/ALTER_PROCEDURE wird verarbeitet

Objekttyp SCHEMA_EXPORT/TABLE/INDEX/INDEX wird verarbeitet

Objekttyp SCHEMA_EXPORT/TABLE/INDEX/FUNCTIONAL_INDEX/INDEX wird verarbeitet

Objekttyp SCHEMA_EXPORT/TABLE/CONSTRAINT/CONSTRAINT wird verarbeitet

Objekttyp SCHEMA_EXPORT/VIEW/VIEW wird verarbeitet

Objekttyp SCHEMA_EXPORT/PACKAGE/PACKAGE_BODY wird verarbeitet

Objekttyp SCHEMA_EXPORT/TABLE/CONSTRAINT/REF_CONSTRAINT wird verarbeitet

Job "SYSTEM"."20260420-2124_imp_prod " um Mo Apr 20 21:28:10 2026 elapsed 0 00:01:23 abgeschlossen

Job abgeschlossen, Status: COMPLETED

 

Ein paar Tips zum Troubleshooting und eine weitere Einsatzmöglichkeit für die Prozedur folgen in der Fortsetzung dieser Kolumne.

 

Elke Fritsch

© Markus Kammermann