sql >> Databasteknik >  >> RDS >> Oracle

Extrahera rader från en DB inklusive beroende rader

Det kan finnas något verktyg som redan gör det, men att godtyckligt extrahera alla radtabeller från en starttabell är en liten utvecklingsuppgift i sig. Jag kan inte skriva hela grejen åt dig, men jag kan få dig igång - jag började skriva det, men efter ungefär 20 minuter insåg jag att det var lite mer arbete som jag ville ägna mig åt ett obetalt svar.

Jag kan se att det görs bäst av en rekursiv PL/SQL-procedur som skulle använda dbms_ouput och user_cons_columns &user_constraints för att skapa inserts-sats för källtabellen. Du kan fuska lite genom att skriva alla infogningar som om kolumnerna vore char-värden, eftersom Oracle implicit kommer att konvertera alla char-värden till rätt datatyp, förutsatt att dina NLS-parametrar är identiska på käll- och målsystemet.

Observera att paketet nedan kommer att få problem om du har cirkulära relationer i dina tabeller; På tidigare versioner av Oracle kan du också få slut på buffertutrymme med dbms_output. Båda problemen kan lösas genom att infoga den genererade SQL-filen i en staging-tabell som har ett unikt index på SQL-filen, och avbryta rekursionen om du får en unik nyckelkollision. Den stora tidsbesparingen nedan är MakeParamList-funktionen, som konverterar en markör som returnerar en lista med kolumner till antingen en kommaseparerad lista eller ett enstaka uttryck som visar värdena för dessa kolumner i en form med citattecken, kommaseparerad form när den körs som välj sats i en fråga mot tabellen.

Observera också att följande paket inte riktigt kommer att fungera förrän du modifierar det ytterligare (en av anledningarna till att jag slutade skriva det):Den initiala infogningssatsen som genereras är baserad på antagandet att argumentet constraint_vals som skickas in kommer att resultera i att en enda rad blir genereras - naturligtvis är detta nästan säkert inte fallet när du börjar återkommande (eftersom du kommer att ha många barnrader för en förälder). Du måste ändra genereringen av den första satsen (och de efterföljande rekursiva anropen) till att vara inne i en loop för att hantera de fall där anropet till det första EXECUTE IMMEDIATE-anropet genererar flera rader istället för en enda. Grunderna för att få det att fungera är här, du behöver bara slipa ut detaljerna och få den yttre markören att fungera.

En sista anmärkning också:Det är osannolikt att du skulle kunna köra den här proceduren för att generera en uppsättning rader som, när de infogas i ett målsystem, skulle resultera i en "ren" uppsättning data, eftersom även om du skulle få alla beroende data, data kan bero på andra tabeller som du inte importerade (t.ex. den första underordnade tabellen du stöter på kan ha andra främmande nycklar som pekar på tabeller som inte är relaterade till din ursprungliga tabell). I så fall kanske du vill börja med detaljtabellerna och arbeta dig uppåt istället för nedåt; Om du gör det, skulle du också vilja vända ordningen till de satser du genererade, antingen med hjälp av ett skriptverktyg eller genom att infoga sql i en mellanställningstabell som jag nämnde ovan, med en sekvens och sedan välja ut den med fallande sortering .

När det gäller att anropa den skickar du den kommaseparerade listan med kolumner för att begränsa som constraint_cols och motsvarande kommaseparerade lista med värden som constraint_vals, t.ex.:

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

Här är den:

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;


  1. MySQL Felaktigt datetime-värde:'0000-00-00 00:00:00'

  2. Hur kontrollerar jag om aktivera/inaktivera nycklar?

  3. MySQL-syntax Fel rätt syntax att använda nära 'desc

  4. hur man använder DISTINCT ON med mysql med ActiveRecord