Domanda

Vorrei generare stringhe di inserimento per una riga nel mio database Oracle, comprese tutte le sue righe dipendenti in altre tabelle (e le loro righe dipendenti).

Esempio:

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)
);

Quando estraggo la riga da a con a_id = 1, il risultato dovrebbe essere una stringa di inserimento per quella riga e le righe dipendenti:

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);

Il motivo per cui voglio fare questo è che ho un grande database con molte tabelle e vincoli diversi tra allora e vorrei estrarre un piccolo sottoinsieme di dati come dati di test.

È stato utile?

Soluzione

Potrebbe esserci qualche strumento che lo fa già, ma estrarre arbitrariamente tutte le tabelle di righe da una tabella di partenza è una piccola attività di sviluppo in sé. Non posso scrivere tutto per te, ma posso farti iniziare - ho iniziato a scriverlo, ma dopo circa 20 minuti, mi sono reso conto che era un po 'più di lavoro che volevo impegnare per una risposta non pagata.

Riesco a vederlo fatto meglio con una procedura PL / SQL ricorsiva che userebbe dbms_ouput e user_cons_columns & amp; vincoli_utente per creare l'istruzione insert per la tabella di origine. Puoi imbrogliare un po 'scrivendo tutti gli inserti come se le colonne fossero valori char, poiché Oracle convertirà implicitamente tutti i valori char nel giusto tipo di dati, supponendo che i tuoi parametri NLS siano identici sul sorgente & amp; sistema di destinazione.

Nota, il pacchetto seguente avrà problemi se hai relazioni circolari nelle tue tabelle; inoltre, nelle versioni precedenti di Oracle, potresti esaurire lo spazio buffer con dbms_output. Entrambi i problemi possono essere risolti inserendo il sql generato in una tabella di gestione temporanea che ha un indice univoco sul sql e interrompendo la ricorsione se si ottiene una collisione chiave univoca. Il grande risparmio di tempo di seguito è la funzione MakeParamList, che converte un cursore che restituisce un elenco di colonne in un elenco separato da virgole o una singola espressione che visualizzerà i valori di tali colonne in un formato tra virgolette separato quando eseguito come selezionare la clausola in una query rispetto alla tabella.

Nota anche che il seguente pacchetto non funzionerà fino a quando non lo modificherai ulteriormente (uno dei motivi per cui ho smesso di scriverlo): L'istruzione insert iniziale generata si basa sul presupposto che l'argomento boundint_vals trasmesso comporterà generazione di una riga singola - ovviamente, questo non è quasi certamente il caso una volta che inizi a ricorrere (poiché avrai molte righe figlio per un genitore). È necessario modificare la generazione della prima istruzione (e le successive chiamate ricorsive) in modo che si trovino all'interno di un ciclo per gestire i casi in cui la chiamata alla prima chiamata EXECUTE IMMEDIATE genera più righe anziché una singola. Le basi per farlo funzionare sono qui, devi solo macinare i dettagli e far funzionare il cursore esterno.

Un'ultima nota anche: è improbabile che tu possa eseguire questa procedura per generare una serie di righe che, se inserite in un sistema di destinazione, comporterebbero un "pulito". insieme di dati, poiché sebbene si ottengano tutti i dati dipendenti, tali dati potrebbero dipendere da altre tabelle che non sono state importate (ad esempio, la prima tabella figlio che si incontra può avere altre chiavi esterne che puntano a tabelle non correlate alla tabella iniziale) . In tal caso, potresti voler iniziare con le tabelle dei dettagli e procedere verso l'alto anziché verso il basso; facendo ciò, vorrai anche invertire l'ordine delle istruzioni che hai generato, utilizzando un'utilità di scripting o inserendo sql in una tabella di gestione temporanea, come ho menzionato sopra, con una sequenza, quindi selezionandolo con un ordinamento decrescente .

Per quanto riguarda l'invocazione, si passa l'elenco di colonne separato da virgole da vincolare come vincolo_coli e l'elenco di valori separato da virgola corrispondente come vincolo_valli, ad es .:

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

Eccolo:

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;

Altri suggerimenti

Uso semplicemente il vecchio SQL per svolgere queste attività: utilizzare le istruzioni select per generare i tuoi inserti:

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;

Penso che DBUnit possa farlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top