Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Extraindo linhas de um banco de dados incluindo linhas dependentes


Pode haver alguma ferramenta que já faça isso, mas extrair arbitrariamente todas as tabelas de linhas de uma tabela inicial é uma pequena tarefa de desenvolvimento em si. Não posso escrever tudo para você, mas posso começar - comecei a escrevê-lo, mas depois de cerca de 20 minutos, percebi que era um pouco mais de trabalho que eu queria comprometer com uma resposta não remunerada.

Eu posso ver isso sendo feito melhor por um procedimento PL/SQL recursivo que usaria dbms_ouput e user_cons_columns &user_constraints para criar a instrução inserts para a tabela de origem. Você pode trapacear um pouco escrevendo todas as inserções como se as colunas fossem valores char, já que o Oracle converterá implicitamente qualquer valor char para o tipo de dados correto, assumindo que seus parâmetros NLS sejam idênticos no sistema de origem e destino.

Observe que o pacote abaixo terá problemas se você tiver relacionamentos circulares em suas tabelas; além disso, em versões anteriores do Oracle, você pode ficar sem espaço de buffer com dbms_output. Ambos os problemas podem ser resolvidos inserindo o sql gerado em uma tabela de teste que possui um índice exclusivo no sql e abortando a recursão se você obtiver uma colisão de chave exclusiva. A grande economia de tempo abaixo é a função MakeParamList, que converte um cursor que retorna uma lista de colunas em uma lista separada por vírgulas ou em uma única expressão que exibirá os valores dessas colunas em um formulário entre aspas e separados por vírgulas quando executado como o cláusula select em uma consulta na tabela.

Observe também que o pacote a seguir não funcionará realmente até que você o modifique ainda mais (uma das razões pelas quais parei de escrevê-lo):A instrução de inserção inicial gerada é baseada na suposição de que o argumento constraint_vals passado resultará em uma única linha sendo gerado - é claro, isso quase certamente não é o caso quando você começa a recursivamente (já que você terá muitas linhas filhas para um pai). Você precisará alterar a geração da primeira instrução (e as chamadas recursivas subsequentes) para estar dentro de um loop para lidar com os casos em que a chamada para a primeira chamada EXECUTE IMMEDIATE gera várias linhas em vez de uma única. O básico para fazê-lo funcionar está aqui, você só precisa refinar os detalhes e fazer o cursor externo funcionar.

Uma observação final também:é improvável que você possa executar este procedimento para gerar um conjunto de linhas que, quando inseridas em um sistema de destino, resultaria em um conjunto de dados "limpo", pois embora você obtenha todos os dados dependentes, isso os dados podem depender de outras tabelas que você não importou (por exemplo, a primeira tabela filha que você encontra pode ter outras chaves estrangeiras que apontam para tabelas não relacionadas à sua tabela inicial). Nesse caso, você pode querer começar com as tabelas de detalhes e ir subindo ao invés de descer; fazendo isso, você também deseja reverter a ordem para as instruções que você gerou, usando um utilitário de script ou inserindo o sql em uma tabela de teste como mencionei acima, com uma sequência, selecionando-a com uma classificação decrescente .

Quanto a invocá-lo, você passa a lista de colunas separadas por vírgula para restringir como constraint_cols e a lista de valores separada por vírgula correspondente como constraint_vals, por exemplo:
exec Data_extractor.MakeInserts ('MYTABLE', 'COL1, COL2', '99, 105')

Aqui está:
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;