Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Extrahieren von Zeilen aus einer DB einschließlich abhängiger Zeilen

Möglicherweise gibt es ein Tool, das dies bereits tut, aber alle Zeilentabellen willkürlich aus einer Starttabelle zu extrahieren, ist eine kleine Entwicklungsaufgabe für sich. Ich kann nicht alles für Sie schreiben, aber ich kann Ihnen den Einstieg erleichtern - ich habe angefangen, es zu schreiben, aber nach etwa 20 Minuten wurde mir klar, dass es ein bisschen mehr Arbeit war, die ich für eine unbezahlte Antwort aufwenden wollte. P>

Ich kann sehen, dass dies am besten durch eine rekursive PL/SQL-Prozedur erreicht wird, die dbms_ouput und user_cons_columns &user_constraints verwenden würde, um eine Inserts-Anweisung für die Quelltabelle zu erstellen. Sie können ein wenig schummeln, indem Sie alle Einfügungen so schreiben, als ob die Spalten Zeichenwerte wären, da Oracle alle Zeichenwerte implizit in den richtigen Datentyp umwandelt, vorausgesetzt, Ihre NLS-Parameter sind auf dem Quell- und Zielsystem identisch.

Beachten Sie, dass das folgende Paket Probleme haben wird, wenn Sie Zirkelbeziehungen in Ihren Tabellen haben; Außerdem kann es sein, dass Ihnen bei früheren Versionen von Oracle der Pufferspeicher mit dbms_output ausgeht. Beide Probleme können gelöst werden, indem die generierte SQL in eine Staging-Tabelle eingefügt wird, die einen eindeutigen Index für die SQL hat, und die Rekursion abgebrochen wird, wenn Sie eine eindeutige Schlüsselkollision erhalten. Die große Zeitersparnis unten ist die MakeParamList-Funktion, die einen Cursor, der eine Liste von Spalten zurückgibt, entweder in eine kommagetrennte Liste oder einen einzelnen Ausdruck umwandelt, der die Werte dieser Spalten in einer durch Kommas getrennten Form in Anführungszeichen anzeigt, wenn sie als ausgeführt wird select-Klausel in einer Abfrage für die Tabelle.

Beachten Sie auch, dass das folgende Paket nicht wirklich funktioniert, bis Sie es weiter modifizieren (einer der Gründe, warum ich aufgehört habe, es zu schreiben):Die anfänglich generierte Insert-Anweisung basiert auf der Annahme, dass das übergebene Argument constraint_vals dazu führt, dass eine einzelne Zeile vorhanden ist generiert - natürlich ist dies mit ziemlicher Sicherheit nicht der Fall, sobald Sie mit der Rekursion beginnen (da Sie viele untergeordnete Zeilen für ein übergeordnetes Element haben werden). Sie müssen die Generierung der ersten Anweisung (und der nachfolgenden rekursiven Aufrufe) so ändern, dass sie sich in einer Schleife befindet, um die Fälle zu behandeln, in denen der Aufruf des ersten EXECUTE IMMEDIATE-Aufrufs mehrere Zeilen anstelle einer einzelnen generiert. Die Grundlagen, um es zum Laufen zu bringen, sind hier, Sie müssen nur die Details ausarbeiten und den äußeren Cursor zum Laufen bringen.

Noch eine abschließende Anmerkung:Es ist unwahrscheinlich, dass Sie diese Prozedur ausführen könnten, um eine Reihe von Zeilen zu generieren, die beim Einfügen in ein Zielsystem zu einer "sauberen" Datenmenge führen würden, da Sie zwar alle abhängigen Daten erhalten würden Daten können von anderen Tabellen abhängen, die Sie nicht importiert haben (z. B. kann die erste untergeordnete Tabelle, auf die Sie stoßen, andere Fremdschlüssel haben, die auf Tabellen verweisen, die nichts mit Ihrer ursprünglichen Tabelle zu tun haben). In diesem Fall möchten Sie vielleicht mit den Detailtabellen beginnen und sich nach oben anstatt nach unten arbeiten; Dabei möchten Sie auch die Reihenfolge der von Ihnen generierten Anweisungen umkehren, entweder mit einem Skript-Dienstprogramm oder indem Sie die SQL-Anweisungen wie oben erwähnt mit einer Sequenz in eine Staging-Tabelle einfügen und sie dann mit einer absteigenden Sortierung auswählen .

Um es aufzurufen, übergeben Sie die kommagetrennte Liste der einzuschränkenden Spalten als Constraint_cols und die entsprechende kommagetrennte Liste der Werte als Constraint_vals, z. B.:

exec Data_extractor.MakeInserts ('MYTABLE', 'COL1, COL2', '99, 105')

Hier ist es:

CREATE OR REPLACE PACKAGE data_extractor
IS
   TYPE column_info IS RECORD(
      column_name   user_tab_columns.column_name%TYPE
   );

   TYPE column_info_cursor IS REF CURSOR
      RETURN column_info;

   FUNCTION makeparamlist(
      column_info   column_info_cursor
    , get_values    NUMBER
   )
      RETURN VARCHAR2;

   PROCEDURE makeinserts(
      source_table      VARCHAR2
    , constraint_cols   VARCHAR2
    , constraint_vals   VARCHAR2
   );
END data_extractor;


CREATE OR REPLACE PACKAGE BODY data_extractor
AS
   FUNCTION makeparamlist(
      column_info   column_info_cursor
    , get_values    NUMBER
   )
      RETURN VARCHAR2
   AS
   BEGIN
      DECLARE
         column_name   user_tab_columns.column_name%TYPE;
         tempsql       VARCHAR2(4000);
         separator     VARCHAR2(20);
      BEGIN
         IF get_values = 1
         THEN
            separator := ''''''''' || ';
         ELSE
            separator := '';
         END IF;

         LOOP
            FETCH column_info
             INTO column_name;

            EXIT WHEN column_info%NOTFOUND;
            tempsql := tempsql || separator || column_name;

            IF get_values = 1
            THEN
               separator := ' || '''''', '''''' || ';
            ELSE
               separator := ', ';
            END IF;
         END LOOP;

         IF get_values = 1
         THEN
            tempsql := tempsql || ' || ''''''''';
         END IF;

         RETURN tempsql;
      END;
   END;

   PROCEDURE makeinserts(
      source_table      VARCHAR2
    , constraint_cols   VARCHAR2
    , constraint_vals   VARCHAR2
   )
   AS
   BEGIN
      DECLARE
         basesql               VARCHAR2(4000);
         extractsql            VARCHAR2(4000);
         tempsql               VARCHAR2(4000);
         valuelist             VARCHAR2(4000);
         childconstraint_vals  VARCHAR2(4000);
      BEGIN
         SELECT makeparamlist(CURSOR(SELECT column_name
                                       FROM user_tab_columns
                                      WHERE table_name = source_table), 0)
           INTO tempsql
           FROM DUAL;

         basesql := 'INSERT INTO ' || source_table || '(' || tempsql || ') VALUES (';

         SELECT makeparamlist(CURSOR(SELECT column_name
                                       FROM user_tab_columns
                                      WHERE table_name = source_table), 1)
           INTO tempsql
           FROM DUAL;

         extractsql := 'SELECT ' || tempsql || ' FROM ' || source_table 
                       || ' WHERE (' || constraint_cols || ') = (SELECT ' 
                       || constraint_vals || ' FROM DUAL)';

         EXECUTE IMMEDIATE extractsql
                      INTO valuelist;

         -- This prints out the insert statement for the root row
         DBMS_OUTPUT.put_line(basesql || valuelist || ');');

         -- Now we construct the constraint_vals parameter for subsequent calls:
         SELECT makeparamlist(CURSOR(  SELECT column_name
                                         FROM user_cons_columns ucc
                                            , user_constraints uc
                                        WHERE uc.table_name = source_table
                                          AND ucc.constraint_name = uc.constraint_name
                                     ORDER BY position)
                             , 1)
           INTO tempsql
           FROM DUAL;

         extractsql := 'SELECT ' || tempsql || ' FROM ' || source_table 
                       || ' WHERE ' || constraint_cols || ' = ' || constraint_vals;

         EXECUTE IMMEDIATE extractsql
                      INTO childconstraint_vals;

         childconstraint_vals := childconstraint_vals;

-- Now iterate over the dependent tables for this table
-- Cursor on this statement:
--    SELECT uc.table_name child_table, uc.constraint_name fk_name
--      FROM user_constraints uc
--         , user_constraints ucp
--     WHERE ucp.table_name = source_table
--      AND uc.r_constraint_name = ucp.constraint_name;

         --   For each table in that statement, find the foreign key 
         --   columns that correspond to the rows
         --   in the parent table
         --  SELECT column_name
         --    FROM user_cons_columns
         --   WHERE constraint_name = fk_name
         --ORDER BY POSITION;

         -- Pass that columns into makeparamlist above to create 
         -- the constraint_cols argument of the call below:

         -- makeinserts(child_table, ChildConstraint_cols, childconstrain_vals);
      END;
   END;
END data_extractor;