sql >> Base de Datos >  >> RDS >> Oracle

Extracción de filas de una base de datos, incluidas las filas dependientes

Puede haber alguna herramienta que ya lo haga, pero extraer arbitrariamente todas las tablas de filas de una tabla de inicio es una pequeña tarea de desarrollo en sí misma. No puedo escribirlo todo por ti, pero puedo ayudarte a comenzar:comencé a escribirlo, pero después de unos 20 minutos, me di cuenta de que era un poco más de trabajo y quería comprometerme con una respuesta no remunerada.

Puedo ver que se hace mejor con un procedimiento PL/SQL recursivo que usaría dbms_ouput y user_cons_columns &user_constraints para crear instrucciones de inserción para la tabla de origen. Puede hacer un poco de trampa escribiendo todas las inserciones como si las columnas fueran valores char, ya que Oracle convertirá implícitamente cualquier valor char al tipo de datos correcto, suponiendo que sus parámetros NLS sean idénticos en el sistema de origen y destino.

Tenga en cuenta que el siguiente paquete tendrá problemas si tiene relaciones circulares en sus tablas; además, en versiones anteriores de Oracle, es posible que se quede sin espacio de búfer con dbms_output. Ambos problemas se pueden resolver insertando el sql generado en una tabla de preparación que tiene un índice único en el sql y abortando la recursividad si obtiene una colisión de clave única. El gran ahorro de tiempo a continuación es la función MakeParamList, que convierte un cursor que devuelve una lista de columnas en una lista separada por comas o en una sola expresión que mostrará los valores de esas columnas entre comillas, separado por comas cuando se ejecuta como el cláusula de selección en una consulta contra la tabla.

Tenga en cuenta también que el siguiente paquete realmente no funcionará hasta que lo modifique más (una de las razones por las que dejé de escribirlo):La declaración de inserción inicial generada se basa en la suposición de que el argumento de restricción_vals pasado dará como resultado una sola fila generado; por supuesto, es casi seguro que este no sea el caso una vez que comience a recurrir (ya que tendrá muchas filas secundarias para un padre). Tendrá que cambiar la generación de la primera declaración (y las llamadas recursivas posteriores) para que esté dentro de un bucle para manejar los casos en los que la llamada a la primera llamada EXECUTE IMMEDIATE genera varias filas en lugar de una sola. Los conceptos básicos para que funcione están aquí, solo necesita pulir los detalles y hacer que el cursor externo funcione.

Una nota final también:es poco probable que pueda ejecutar este procedimiento para generar un conjunto de filas que, cuando se insertan en un sistema de destino, darían como resultado un conjunto de datos "limpio", ya que aunque obtendría todos los datos dependientes, eso los datos pueden depender de otras tablas que no importó (por ejemplo, la primera tabla secundaria que encuentre puede tener otras claves externas que apuntan a tablas que no están relacionadas con su tabla inicial). En ese caso, es posible que desee comenzar con las tablas de detalles y avanzar hacia arriba en lugar de hacia abajo; al hacer eso, también querrá invertir el orden de las declaraciones que generó, ya sea usando una utilidad de secuencias de comandos o insertando el sql en una tabla de preparación como mencioné anteriormente, con una secuencia, luego seleccionándola con un orden descendente .

En cuanto a invocarlo, pasa la lista de columnas separadas por comas para restringir como constrict_cols y la correspondiente lista de valores separados por comas como constrict_vals, por ejemplo:

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

Aquí 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;