sql >> Databáze >  >> RDS >> Oracle

Extrahování řádků z DB včetně závislých řádků

Může existovat nějaký nástroj, který to již dělá, ale libovolně extrahovat všechny tabulky řádků z počáteční tabulky je samo o sobě malý vývojový úkol. Nemohu pro vás napsat celou věc, ale mohu vám pomoci začít - začal jsem to psát, ale asi po 20 minutách jsem si uvědomil, že je to trochu více práce, kterou jsem chtěl svěřit neplacené odpovědi.

Vidím to nejlépe pomocí rekurzivní procedury PL/SQL, která by k vytvoření příkazu inserts pro zdrojovou tabulku používala dbms_ouput a user_cons_columns &user_constraints. Můžete trochu podvádět tím, že zapíšete všechny vložky, jako by sloupce byly hodnoty znaků, protože Oracle implicitně převede všechny hodnoty znaků na správný datový typ za předpokladu, že vaše parametry NLS jsou ve zdrojovém a cílovém systému totožné.

Všimněte si, že balíček níže bude mít problémy, pokud máte v tabulkách kruhové vztahy; v dřívějších verzích Oracle také může dojít k nedostatku vyrovnávací paměti pomocí dbms_output. Oba problémy lze vyřešit vložením vygenerovaného sql do pracovní tabulky, která má jedinečný index na sql, a zrušením rekurze, pokud získáte jedinečnou kolizi klíče. Velkou úsporou času níže je funkce MakeParamList, která převádí kurzor, který vrací seznam sloupců, buď na seznam oddělený čárkami, nebo na jeden výraz, který při spuštění jako klauzuli select v dotazu na tabulku.

Všimněte si také, že následující balíček nebude ve skutečnosti fungovat, dokud jej dále neupravíte (jeden z důvodů, proč jsem jej přestal psát):Počáteční vygenerovaný příkaz insert je založen na předpokladu, že předaný argument constraint_vals bude mít za následek vytvoření jediného řádku vygenerováno - samozřejmě to téměř jistě neplatí, jakmile začnete opakovat (protože budete mít mnoho podřízených řádků pro rodiče). Budete muset změnit generování prvního příkazu (a následných rekurzivních volání) tak, aby bylo uvnitř smyčky, abyste zvládli případy, kdy volání prvního volání EXECUTE IMMEDIATE generuje více řádků namísto jednoho. Základy fungování jsou zde, stačí vybrousit detaily a zprovoznit vnější kurzor.

Ještě jedna poznámka na závěr:Je nepravděpodobné, že byste mohli spustit tento postup pro vygenerování sady řádků, které by po vložení do cílového systému vedly k „čisté“ sadě dat, protože ačkoliv byste získali všechna závislá data, data mohou záviset na jiných tabulkách, které jste neimportovali (např. první podřízená tabulka, se kterou se setkáte, může mít jiné cizí klíče, které ukazují na tabulky nesouvisející s vaší počáteční tabulkou). V takovém případě možná budete chtít začít s podrobnými tabulkami a postupovat nahoru místo dolů; když to uděláte, také byste chtěli obrátit pořadí příkazů, které jste vygenerovali, buď pomocí skriptovacího nástroje, nebo vložením sql do pracovní tabulky, jak jsem zmínil výše, se sekvencí a poté jej vybrat sestupným řazením .

Pokud jde o jeho vyvolání, předáte čárkami oddělený seznam sloupců k omezení jako constraint_cols a odpovídající seznam hodnot oddělených čárkami jako constraint_vals, např.:

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

Tady to je:

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. Zvýraznění syntaxe v prohlížeči Oracle něco jako SQL Server Management Studio

  2. Jak vytvořit uloženou proceduru v SQL Server

  3. Měl by existovat jeden SQLiteOpenHelper pro každou tabulku v databázi?

  4. Jak nainstalovat MySQL Workbench na Ubuntu