Oracle’s Little-Known Multi-Table Insert

Posted in: Technical Track

We are assisting one of our clients with a data conversion project and have used a feature that I was surprised so few people knew about. It is the multi-table insert.

Have you ever wanted to select a set of data and then insert different portions of it into different tables? How did you do it? If you are like many developers, you may have opened a cursor loop to select the data, and based on different conditions, used the data in different insert statements.

Let’s assume we are re-architecting our data, and that we have the classic emp and dept tables. We want to put into an exception table for further investigation, any employee whose commission is greater than their salary. In addition, any departments that have no employees should also be placed in exception tables. The old way of doing this might have looked like:

DECLARE
   emp_count number;
BEGIN
FOR dept_rec in ( select dept_id, dept_name from dept ) LOOP
    emp_count := 0;
    FOR emp_rec in ( select emp_id id, emp_name name, sal, comm where dept_id = dept_rec.dept_id ) LOOP
        emp_count := emp_count + 1;
        IF comm > sal THEN 
           insert into emp_exception 
           values ( emp_rec.id, emp_rec.name, emp_rec.sal, emp_rec.comm, emp_rec.dept_id, 'comm greater than sal');
        ELSE
           insert into new_emp 
            values ( emp_rec.id, emp_rec.name, emp_rec.dept_id, emp_rec.sal, emp_rec.comm);
        END IF;
    END LOOP;
    IF emp_count = 0 THEN
       insert into dept_exception
       values ( dept_rec.dept_id, dept_rec.dept_name, 'No employees');
    ELSE
       insert into new_dept
       values ( dept_rec.dept_id, dept_rec.dept_name);
    END IF;
END LOOP;
COMMIT;
END:
/

Using outer joins, the select can be converted to, and the code changed to:

DECLARE
   emp_count number;
BEGIN
FOR rec in ( select dept.dept_id, dept_name, emp_id id, emp_name name, sal, comm 
               from dept , emp
              where dept.dept_id = emp.dept_id(+)) LOOP

    IF rec.id is NULL THEN
       insert into dept_exception
       values ( dept_rec.dept_id, dept_rec.dept_name, 'No employees');
    ELSE
       insert into new_dept
       values ( rec.dept_id, rec.dept_name);
        IF rec.comm > rec.sal THEN 
           insert into emp_exception 
           values ( rec.id, rec.name, rec.sal, rec.comm, rec.dept_id, 'comm greater than sal');
        ELSE
           insert into new_emp 
            values ( rec.id, rec.name, rec.dept_id, rec.sal, rec.comm);
        END IF;
END LOOP;
COMMIT;
END:
/

Okay. I admit the sample code so far is a bit contrived, but I needed something easy and simple to show the logical progression to the multi-table insert. I have broken down the PL/SQL loop into two insert statements. The first replaces the above logic and the second will load the department records, as I want to load only one department record for each department.

INSERT 
   when ( id is null ) 
       insert into dept_exception
       values ( dept_rec.dept_id, dept_rec.dept_name, 'No employees')
   when ( comm > sal ) 
       insert into emp_exception 
       values ( rec.id, rec.name, rec.sal, rec.comm, rec.dept_id, 'comm greater than sal')
   when ( first_dept = 'Y')
       insert into new_dept 
       values ( dept_id, dept_name)
   when ( nvl(sal,0) >= nvl(comm,0) )
           insert into new_emp 
           values ( id, name, dept_id, sal, comm)
SELECT dept.dept_id, dept_name, emp_id id, emp_name name, sal, comm 
               from dept , emp
              where dept.dept_id = emp.dept_id(+);

INSERT into new_dept
  SELECT dept_id, dept_name from dept
  MINUS
  SELECT dept_id, dept_name from new_dept;

I then tested this with the /*+ APPEND */ hint and set autotrace on. It shows that this also works with NOLOGGING tables and that the amount of redo is considerably reduced (as expected with NOLOGGING). I then created the test tables with CLOBS and confirmed that the multi-table insert also worked with tables with CLOBs.

Interested in working with Babette? Schedule a tech call.

About the Author

Senior DBA Babette first joined Pythian in February 2000 and except for two short stints as an independent contractor for the Canadian government has been working with us ever since.

1 Comment. Leave new

Paul Vallee
May 1, 2007 3:38 pm

Andrew Clarke riffs on this post and adds a lot of useful thoughts on it at his blog http://radiofreetooting.blogspot.com/2007/05/another-use-for-insert-all-syntax.html

Reply

Leave a Reply

Your email address will not be published. Required fields are marked *