简体   繁体   中英

How to insert into two tables at the same time

I hope I am able to explain this properly. I need to insert data into two DB tables. This data is already stored in two other tables but needs to be migrated. To simplify things lets say here are my source tables:

folder with columns folderid, foldername

link with columns linkid, url and folderid

And my destination tables are:

new_folder with columns folderid, foldername

new_link with columns linkid, url and folderid

There are other columns also but they are not important. My problem is that whoever setup the original database used a random number generator in java to create the folder and link ids and these numbers are complete garbage. They need to be replaced with a number generated by a sequence which exists in the DB.

So what I need is one statement that does something like the following:

insert into new_folder(folderid, foldername), new_link(linkid, url, folderid)
values (select seq_folder_id.nextval, foldername, seq_link_id.nextval, url, seq_folder_id.currval from folder, link where folder.folderid = link.folderid).

There could be multiple links associated with the folder. There are 200k rows in the folder table and 10 times that in the link table so I need some sort of a script that will run and pull out all folders and create new entries with the new sequence as the ID. That on its own would be fine but if I just do that then I have no way of mapping the links to the folders once the ID is changed unless I do it in the one statement if that makes sense.

Here is a good tutorial on multi-table inserts:

http://www.oracle-developer.net/display.php?id=209

The basic structure is:

INSERT ALL|FIRST
   [WHEN condition THEN] INTO target [VALUES]
   [WHEN condition THEN] INTO target [VALUES]
   ...
   [ELSE] INTO target [VALUES]
SELECT ...
FROM   source_query;

You will probably want to restructure your source subquery to create a column that indicates the first row for each foldername that you can then use in the when conditions to only insert one row per folder to the folder table, and then all rows to the link table. You will also have to re-jig how you are generating the IDs so that you have the same folderID for each link row. In other words, time you learned aggregate functions!

Example: look at this query and see if it would help you figure it out (hint, folder_rank can become the basis for the folder_id, and how group_rank could drive a when clause to figure out the folder insert)

select dense_rank() over (order by foldername) as folder_rank,
       rank() over (parition by foldername order  by url) as group_rank
     , foldername
     , seq_link_id.nextval
     , url 
from folder, link 
where folder.folderid = link.folderid

Alternately, look at doing this in a chunk of pl/sql rather than pure SQL:

declare
  l_new_folder_id new_folder.folder_id%type;
begin
   for folder_Rec in (select folder_id, foldername from folder)
   loop
      insert into new_folder (folder_id, folder_name) 
      values ( seq_folder_id.nextval, folder_Rec.folder_name) 
      returning folder_id into l_new_folder_id;

      for link_rec in (select url from link where folder_id = folder_rec.folder_id)
          insert into new_link (link_id, folder_id, url) 
          values (seq_link_id.nextval, l_new_folder_id, link_rec.url);
      end loop;
    end loop;
    commit;
end;

There are several ways to skin this cat, and complex SQL may not be the best option for a beginner. Also, check your source data - if you have folders that do not have any links then the pure SQL approach will also have to incoporate an outer join rather than the current definition if you want the link-less folders to be inserted. The PL/Sql solution will already handle that scenario.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM