views:

581

answers:

4

I am trying to write a data migration pl/sql script to transfer some of the data in one schema to a different schema on another server. The second database started as a subset of the original database, but we have modified the schema. So I can't just use the following for each table:

Insert into DB2.table_name select * from DB1.table_name2;

I have tried doing a search for sample scripts that show how to do this, but couldn't find anything.

+2  A: 

If the columns are different between DB1.table_name and DB2.table_name then you're going to have to specify a column list in the insert statement. Unfortunately, there's not really a "magic bullet" here.

With that said, to speed up the process you could write some PL/SQL to generate the insert statements, and then you could fix those by hand. Here's a sample PL/SQL code to do this. In this example, l_src_table would be your source table and l_target_table would be your target table. Obviously, you'll still have to manually fix the SQL statement this code generates, but this will at least generate a template SQL which should save you a lot of time.

DECLARE
  l_insert_stmt VARCHAR2(4000);
  l_comma VARCHAR2(1) DEFAULT ' ';
  l_src_table VARCHAR2(500) := 'TABLE1';
  l_src_table_owner VARCHAR2(500) := 'DB1';
  l_target_table VARCHAR2(500) := 'TABLE2';
  l_target_table_owner VARCHAR2(500) := 'DB2';
BEGIN
  l_insert_stmt := 'INSERT INTO ' || l_target_table || ' ( ';
  FOR rec IN (SELECT column_name FROM all_tab_columns
              WHERE TABLE_name = l_target_table AND owner = l_target_table_owner)
  LOOP
     l_insert_stmt := l_insert_stmt || l_comma || rec.column_name;
     l_comma := ',';
  END LOOP;
  l_insert_stmt := l_insert_stmt || ' ) ';

  l_insert_stmt := l_insert_stmt || ' SELECT ';
  l_comma := ' ';
  FOR rec IN (SELECT column_name FROM all_tab_columns
              WHERE TABLE_name = l_src_table AND owner = l_src_table_owner)
  LOOP
     l_insert_stmt := l_insert_stmt || l_comma || rec.column_name;
     l_comma := ',';
  END LOOP;
  l_insert_stmt := l_insert_stmt || ' FROM ' || l_src_table;

  dbms_output.put_line(l_insert_stmt);
END;
dcp
I tried the part to output just the Insert into (without the select). All I get output for each table is "Insert into table_name ( )" ...none of the column names are output.
Theresa
On the right track, but you aren't accounting for the ordering of the columns or that the column lists are different.
Adam Hawkes
You need to make sure to set the l_src_table, l_src_table_owner, l_target_table VARCHAR2(500), l_target_table_owner variables properly or it won't work. For example, you could try setting the values to this: l_src_table VARCHAR2(500) := 'ALL_OBJECTS'; l_src_table_owner VARCHAR2(500) := 'SYS'; l_target_table VARCHAR2(500) := 'ALL_TABLES'; l_target_table_owner VARCHAR2(500) := 'SYS';This should at least give you a SQL statement with columns in it. Then you can just tweak the values to whatever's proper for your environment.
dcp
@Adam Hawkes - The OP already said the schemas were different, so by definition we can already assume that the column lists will be different. If that were not the case (i.e. if the tables were the same between schemas), she could have just used the "insert into select from.." without listing any columns and we wouldn't be having this discussion :).
dcp
I like this idea! Unfortunately I couldn't get it to output the column names for me. I will just have to do a cut and paste from SQL Developer, or type them in.
Theresa
Yes, one thing I didn't take into account here was the fact that you were going across database links. A database dictionary (i.e. the all_tab_columns is part of the database dictionary) is specific to each DB instance, so you'd need to access the database dictionary for each database. So instead of "SELECT column_name FROM all_tab_columns", you'd need to use "SELECT column_name FROM all_tab_columns@DB1", for example. Give that a try on the script above and hopefully it can work for you.
dcp
Correction: you may need to use sys.all_tab_columns@DB1I don't have any DBs here to test with that have links, so I'm not totally sure (it may work without sys on the front, but if not, you can try adding it).
dcp
A: 

If you need to do this often enough,then another option is to use a schema synchronization tool. Toad, dbsolo and probably a few other tools can be used. It has saved me a lot of time and effort.

Dinesh Bhat
This is one-time only. We have a small app that was created as part of another (very large) project. They share a database (and some of the tables). Now we are moving the small app to its own database. There is extra information in the original database that we don't want/need in the small app. They have different schemas.
Theresa
I understand. In some cases a $100 investment is worth the productivity gained in development and reduced maintenance, if the problem is complicated enough.
Dinesh Bhat
A: 

You are looking for dbms_magic.perform_data_migration(old_schema, new_schema).

erikkallen
I would have been really annoyed if that actually existed :)
chris
+1  A: 

You can create a database link.

Then, if you're trying to migrate from db1 to db2:

Insert into table_name (f1, f2, etc) select (f1, f2, etc) from table_name2@DB2;

The select can be as complex or simple as needed.

chris