Retrieving rows from a database, including defrand rows

I would like to generate insert rows for a row in my Oracle database, including all its dependent rows in other tables (and their dependent rows).

Example:

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

When I retrieve a row from a using a_id = 1, the result should be an insert row for this row and dependent rows:

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

The reason I want to do this is because I have a large database with many different tables and restrictions between them, and I would like to extract a small subset of the data as test data.

+4
source share
3 answers

There may be some tool that does this already, but to arbitrarily extract all the row tables from the start table is a small development task in itself. I can’t write all this for you, but I can start with you - I started writing, but after about 20 minutes I realized that it was a little work that I wanted to do with an unpaid answer.

I see that this is done best with the recursive PL / SQL procedure, which will use dbms_ouput and user_cons_columns and user_constraints to create the inserts statement for the source table. You can trick it a bit by writing all the inserts as if the columns were char values, since Oracle will implicitly convert any char values ​​to the right data type, assuming that your NLS parameters are identical in the source and target systems.

Note that there will be problems in the package below if you have circular relationships in your tables; In addition, in earlier versions of Oracle, you can exit the buffer using dbms_output. Both problems can be solved by inserting the generated sql into a staging table with a unique index in sql and interrupting the recursion if you get a unique key clash. A screensaver with a longer time below is the MakeParamList function, which converts a cursor that returns a list of columns to a comma-separated list, or a single expression that will display the values ​​of these columns in a quoted form with a comma when run as select in a table query.

Please also note that the following package will not work until you change it (one of the reasons I stopped writing it): The initial initialization statement, based on the assumption that the constraint_vals argument is passed, will result in one line being generated - of course, this is almost not the case as soon as you start recursing (since you will have many child lines for the parent). You will need to change the generation of the first statement (and subsequent recursive calls) inside the loop in order to handle cases where calling the first EXECUTE IMMEDIATE call generates several rows instead of one. The basics of its work here, you just need to grind the details and make the external cursor work.

Finally, note: it is unlikely that you can run this procedure to create a set of rows that, when inserted into the target system, will lead to β€œclean” data sets, because although you get all the dependent data, the data may depend on other tables that you not imported (for example, the first child table you encounter may have other foreign keys that point to tables that are not related to your original table). In this case, you can start with detailed tables and work up, not down; by doing this, you also want to reverse the order for the instructions you created, either by using the scripting utility, or by inserting sql into the staging table, as I mentioned above, with a sequence, and then selecting it with a descending sort.

As for calling it, you pass a list of columns separated by commas, to limit as constraint_cols and the corresponding list of values ​​separated by commas, as restrictions, for example:

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

Here he is:

 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; 
+12
source

I just use plain old SQL to do these tasks - use select statements to create your inserts:

 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; 
+1
source

I think DBUnit can do this.

0
source

Source: https://habr.com/ru/post/1286582/


All Articles