Question

J'aimerais générer des chaînes d'insertion pour une ligne de ma base de données Oracle, y compris toutes les lignes dépendantes d'autres tables (et leurs lignes dépendantes).

Exemple:

CREATE TABLE a (
  a_id number PRIMARY KEY,
  name varchar2(100)
);
CREATE TABLE b (
  b_id number PRIMARY KEY,
  a_id number REFERENCES a(a_id)
);

Lorsque j'extrais la ligne de a avec a_id = 1, le résultat devrait être une chaîne d'insertion pour cette ligne et les lignes dépendantes:

INSERT INTO a(a_id, name) VALUES (1, 'foo');
INSERT INTO b(b_id, a_id) VALUES (1, 1);
INSERT INTO b(b_id, a_id) VALUES (2, 1);
INSERT INTO b(b_id, a_id) VALUES (3, 1);

La raison pour laquelle je veux faire cela est que j'ai une grande base de données avec beaucoup de tables et de contraintes différentes entre celles-ci et j'aimerais extraire un petit sous-ensemble de données en tant que données de test.

Était-ce utile?

La solution

Certains outils le font peut-être déjà, mais extraire arbitrairement toutes les tables de lignes d'une table de départ est une petite tâche de développement en soi. Je ne peux pas tout écrire pour vous, mais je peux vous aider à commencer. J'ai commencé à l'écrire, mais au bout de 20 minutes environ, j'ai réalisé que c'était un peu plus de travail que je voulais engager pour une réponse impayée.

Je vois que cela est mieux réalisé par une procédure PL / SQL récursive qui utiliserait dbms_ouput et user_cons_columns & amp; user_constraints pour créer une instruction inserts pour la table source. Vous pouvez tricher un peu en écrivant toutes les insertions comme si les colonnes étaient des valeurs de caractères, car Oracle convertira implicitement toutes les valeurs de caractères en un type de données correct, en supposant que vos paramètres NLS soient identiques sur le code source & amp; système cible.

Remarque: le package ci-dessous rencontrera des problèmes si vous avez des relations circulaires dans vos tables. De plus, sur les versions antérieures d'Oracle, vous pouvez manquer d'espace tampon avec dbms_output. Les deux problèmes peuvent être résolus en insérant le SQL généré dans une table de transfert ayant un index unique sur le SQL et en abandonnant la récursion si vous obtenez une collision de clé unique. Le gros gain de temps ci-dessous est la fonction MakeParamList, qui convertit un curseur qui renvoie une liste de colonnes en une liste séparée par des virgules, ou en une expression unique qui affiche les valeurs de ces colonnes dans un formulaire entre guillemets et séparé par une virgule clause select dans une requête sur la table.

Notez également que le package suivant ne fonctionnera pas réellement tant que vous ne le modifierez pas davantage (une des raisons pour lesquelles j'ai arrêté de l'écrire): L'instruction insert initiale générée est basée sur l'hypothèse que l'argument constraint_vals transmis aboutira à une une seule ligne étant générée - bien sûr, ce n’est presque certainement pas le cas une fois que vous commencez à récurer (car vous aurez plusieurs lignes enfants pour un parent). Vous devrez modifier la génération de la première instruction (et des appels récursifs suivants) pour qu'elle se trouve dans une boucle afin de gérer les cas où l'appel du premier appel EXECUTE IMMEDIATE génère plusieurs lignes au lieu d'une seule. Les bases du bon fonctionnement sont ici, il vous suffit de préciser les détails et de faire en sorte que le curseur extérieur fonctionne.

Une dernière remarque également: il est peu probable que vous puissiez exécuter cette procédure pour générer un ensemble de lignes qui, une fois insérées dans un système cible, donneraient lieu à un "nettoyage". ensemble de données, car bien que vous obteniez toutes les données dépendantes, ces données peuvent dépendre d'autres tables que vous n'avez pas importées (par exemple, la première table enfant que vous rencontrerez peut avoir d'autres clés étrangères qui pointent vers des tables non liées à votre table initiale) . Dans ce cas, vous voudrez peut-être commencer par les tableaux détaillés et progresser progressivement plutôt que vers le bas; ce faisant, vous voudriez également inverser l'ordre avec les instructions que vous avez générées, à l'aide d'un utilitaire de script ou en insérant SQL dans une table de transfert, comme je l'ai mentionné ci-dessus, avec une séquence, puis en le sélectionnant avec un tri décroissant. .

Comme pour l'invoquer, vous passez la liste des colonnes séparées par des virgules à contraindre en tant que constraint_cols et la liste de valeurs correspondante séparée par des virgules en tant que constraint_vals, par exemple:

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

La voici:

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;

Autres conseils

J'utilise simplement du vieux SQL pur pour effectuer ces tâches - utilisez les instructions select pour générer vos insertions:

set pagesize 0
set verify off

  SELECT 'INSERT INTO a(a_id, name) VALUES ('
         || a_id || ', '
         || '''' || name || ''');'
    FROM a
   WHERE a_id = &&1;

  SELECT 'INSERT INTO b(b_id, a_id) VALUES ('
         || b_id || ', '
         || a_id || ');'
    FROM b
   WHERE a_id = &&1;

Je pense que DBUnit peut le faire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top