Call now: 252-767-6166  
Oracle Training Oracle Support Development Oracle Apps

 
 Home
 E-mail Us
 Oracle Articles
New Oracle Articles


 Oracle Training
 Oracle Tips

 Oracle Forum
 Class Catalog


 Remote DBA
 Oracle Tuning
 Emergency 911
 RAC Support
 Apps Support
 Analysis
 Design
 Implementation
 Oracle Support


 SQL Tuning
 Security

 Oracle UNIX
 Oracle Linux
 Monitoring
 Remote s
upport
 Remote plans
 Remote
services
 Application Server

 Applications
 Oracle Forms
 Oracle Portal
 App Upgrades
 SQL Server
 Oracle Concepts
 Software Support

 Remote S
upport  
 Development  

 Implementation


 Consulting Staff
 Consulting Prices
 Help Wanted!

 


 Oracle Posters
 Oracle Books

 Oracle Scripts
 Ion
 Excel-DB  

Don Burleson Blog 


 

 

 


 

 

 
 

Bulk Collection of DML Results


Oracle Tips by Burleson Consulting

 

The following Tip is from the outstanding book "Oracle PL/SQL Tuning: Expert Secrets for High Performance Programming" by Dr. Tim Hall, Oracle ACE of the year, 2006:

The RETURNING clause is used to return specific columns from rows manipulated by DML statements.  When DML statements manipulate multiple rows, the data returned can be loaded into a collection using a bulk operation.  The returning_bulk_collect.sql script provides an example of this functionality.

returning_bulk_collect.sql

SET SERVEROUTPUT ON
DECLARE
  TYPE t_object_id_tab IS TABLE OF bulk_collect_test.object_id%TYPE;

  l_tab  t_object_id_tab;
BEGIN
  DELETE FROM bulk_collect_test
  RETURNING object_id BULK COLLECT INTO l_tab;

  DBMS_OUTPUT.put_line(‘Deleted IDs : ' || l_tab.count || ' rows');

  ROLLBACK;
END;
/

The returning_bulk_collect.sql script defines a collection, deletes the data from the test table and returns the OBJECT_IDs of the deleted rows.  To preserve the data, the script performs a rollback, allowing for multiple runs.  The output from this script shows that the collection is populated as expected.

SQL> @returning_bulk_collect.sql
Deleted IDs : 61202 rows

The FORALL command can be used in conjunction with a DML statement with a RETURNING clause.  In this situation, both the DML and the return data are bulk operations.  The forall_returning_bulk_collect.sql script provides an example of this functionality.

forall_returning_bulk_collect.sql

SET SERVEROUTPUT ON
DECLARE
  TYPE t_object_id_tab IS TABLE OF bulk_collect_test.object_id%TYPE;

  l_in_tab   t_object_id_tab;
  l_out_tab  t_object_id_tab;
BEGIN
  -- Populate collection use in forall.
  SELECT object_id
  BULK COLLECT INTO l_in_tab
  FROM   bulk_collect_test
  WHERE  rownum < 101; 

  FORALL i IN l_in_tab.first .. l_in_tab.last
    DELETE FROM bulk_collect_test
    WHERE  object_id = l_in_tab(i)
    RETURNING object_id BULK COLLECT INTO l_out_tab;

  DBMS_OUTPUT.put_line('Starting IDs : ' || l_in_tab.count || ' rows');
  DBMS_OUTPUT.put_line('Deleted IDs  : ' || l_out_tab.count || ' rows');

  ROLLBACK;
END;
/

The forall_returning_bulk_collect.sql script defines two collections, one to support the FORALL statement and one to support the RETURNING clause.  The first collection is populated to provide IDs of rows to be deleted. Next the bulk delete is performed and the data returned into the second collection.  Then the size of both collections is reported.  The output from this script is displayed below.

SQL> @forall_returning_bulk_collect.sql
Starting IDs : 100 rows
Deleted IDs  : 100 rows

PL/SQL procedure successfully completed.

As expected, the contents of the input and output collections are identical.  The next section investigates the use of bulk DML operations using the FORALL statement.

FORALL in PL/SQL

The FORALL statement allows insert, update and delete statements to be bound to collections in a single operation, resulting in less communication between the PL/SQL and SQL engines.  As with the BULK COLLECT option, this reduction in context switches between the two engines results in better performance.

The following examples show how bulk DML operations are coded and the performance gains when using them.  The examples are based around the FORALL_TEST table which can be created using the create_forall_test.sql script listed below.

create_forall_test.sql
CREATE TABLE forall_test (
  id           NUMBER(10),
  code         VARCHAR2(10),
  description  VARCHAR2(50));

ALTER TABLE forall_test ADD (
  CONSTRAINT forall_test_pk PRIMARY KEY (id));

ALTER TABLE forall_test ADD (
  CONSTRAINT forall_test_uk UNIQUE (code));

First compare the performance of conventional and bulk insert operations.

Bulk INSERT Operations

With the forall_test table created, the performance of individual inserts is compared against bulk inserts using the insert_forall.sql script listed below. 

The VALUES clause of the bulk operation does not reference the individual elements of the collection.  This syntax was introduced in Oracle9i Release 2 to allow bulk operations to use record structures.

insert_forall.sql

SET SERVEROUTPUT ON
DECLARE
  TYPE t_forall_test_tab IS TABLE OF forall_test%ROWTYPE;

  l_tab    t_forall_test_tab := t_forall_test_tab();
  l_start  NUMBER;
  l_size   NUMBER            := 10000;
BEGIN
  -- Populate collection.
  FOR i IN 1 .. l_size LOOP
    l_tab.extend;

    l_tab(l_tab.last).id          := i;
    l_tab(l_tab.last).code        := TO_CHAR(i);
    l_tab(l_tab.last).description := 'Description: ' || TO_CHAR(i);
  END LOOP;

  EXECUTE IMMEDIATE ‘TRUNCATE TABLE forall_test’;

  -- Time regular inserts.

  l_start := DBMS_UTILITY.get_time;

  FOR i IN l_tab.first .. l_tab.last LOOP
    INSERT INTO forall_test (id, code, description)
    VALUES (l_tab(i).id, l_tab(i).code, l_tab(i).description);
  END LOOP;

  DBMS_OUTPUT.put_line('Normal Inserts: ' ||
                       (DBMS_UTILITY.get_time - l_start)); 

  EXECUTE IMMEDIATE ‘TRUNCATE TABLE forall_test’;

  -- Time bulk inserts. 

  l_start := DBMS_UTILITY.get_time;

  FORALL i IN l_tab.first .. l_tab.last
    INSERT INTO forall_test VALUES l_tab(i);

  DBMS_OUTPUT.put_line('Bulk Inserts  : ' ||
                       (DBMS_UTILITY.get_time - l_start));

  COMMIT;
END;
/

The insert_forall.sql script is split into four main sections:

  • Declaration - A table type is defined with the same structure as the test table, which is then used to declare and initialize a collection.  The collection is initialized to support the manual operation, not the bulk operation as these implicitly initialize collections.  In addition variables are defined to store the start time of each operation and the size of the collection.

  • Populate Collection – Extends and populates the collection to the desired size.

  • Time Regular Inserts – Loops through the collection performing a separate insert for each row and displays the time taken to complete the loop.

  • Time Bulk Inserts – Performs a bulk operation to bind the collection into an insert statement and displays the time taken to complete the operation.

The types of results expected from the script are shown below, in this case, using a collection of 10,000 records.  The times will vary between servers and individual runs depending on the specification and usage levels of the servers.  The thing to focus on is not the absolute speed, but the comparison between the two.

SQL> @insert_forall.sql
Normal Inserts: 300
Bulk Inserts  : 19

PL/SQL procedure successfully completed.

The output shows it takes approximately 3 seconds to insert 10,000 rows via a conventional loop, while the bulk operation completes that same amount of work in approximately 0.2 seconds.  This is a dramatic illustration of the benefits of bulk operations.

For the sake of completeness, the insert_forall_8i.sql script shows how this operation would be coded prior to Oracle9i Release 2.  Notice that a separate collection is defined for each column referenced in the bind operation.

insert_forall_8i.sql

SET SERVEROUTPUT ON
DECLARE
  TYPE t_id_tab IS TABLE OF forall_test.id%TYPE;
  TYPE t_code_tab IS TABLE OF forall_test.code%TYPE;
  TYPE t_desc_tab IS TABLE OF forall_test.description%TYPE;

  l_id_tab    t_id_tab   := t_id_tab();
  l_code_tab  t_code_tab := t_code_tab();
  l_desc_tab  t_desc_tab := t_desc_tab();
  l_start     NUMBER;
  l_size      NUMBER     := 10000;
BEGIN
  -- Populate collection.
  FOR i IN 1 .. l_size LOOP
    l_id_tab.extend;
    l_code_tab.extend;
    l_desc_tab.extend;

    l_id_tab(l_id_tab.last)     := i;
    l_code_tab(l_id_tab.last)   := TO_CHAR(i);
    l_desc_tab(l_desc_tab.last) := 'Description: ' || TO_CHAR(i);
  END LOOP;

  EXECUTE IMMEDIATE ‘TRUNCATE TABLE forall_test’;

  -- Time regular inserts.
  l_start := DBMS_UTILITY.get_time;

  FOR i IN l_id_tab.first .. l_id_tab.last LOOP
    INSERT INTO forall_test (id, code, description)
    VALUES (l_id_tab(i), l_code_tab(i), l_desc_tab(i));
  END LOOP;

  DBMS_OUTPUT.put_line('Normal Inserts: ' ||
                       (DBMS_UTILITY.get_time - l_start)); 

  EXECUTE IMMEDIATE ‘TRUNCATE TABLE forall_test’;

  -- Time bulk inserts. 

  l_start := DBMS_UTILITY.get_time;

  FORALL i IN l_id_tab.first .. l_id_tab.last
    INSERT INTO forall_test (id, code, description)
    VALUES (l_id_tab(i), l_code_tab(i), l_desc_tab(i));

  DBMS_OUTPUT.put_line('Bulk Inserts  : ' ||
                       (DBMS_UTILITY.get_time - l_start));
  COMMIT;
END;
/

The same performance improvements are also evident using this method.

SQL> @insert_forall_8i.sql
Normal Inserts: 274
Bulk Inserts  : 24

PL/SQL procedure successfully completed.

The next section compares the performance of conventional and bulk update operations.

 

Comments:

Depending on the release of Oracle BULK DMLs might not be supported across dblinks. According to Oracle, bulk collects over links are supported as of Oracle 9i.

 

This is an excerpt from the bestselling book "Oracle PL/SQL Tuning: Expert Secrets for High Performance Programming" by Dr. Tim Hall, Oracle ACE of the year, 2006.

You can buy the book for only $23.95 (30%-off) when you buy directly from the publisher, and you also get instant access to the code depot of PL/SQL tuning scripts:


 

 
��  
 
 
Oracle Training at Sea
 
 
 
 
oracle dba poster
 

 
Follow us on Twitter 
 
Oracle performance tuning software 
 
Oracle Linux poster
 
 
 

 

Burleson is the American Team

Note: This Oracle documentation was created as a support and Oracle training reference for use by our DBA performance tuning consulting professionals.  Feel free to ask questions on our Oracle forum.

Verify experience! Anyone considering using the services of an Oracle support expert should independently investigate their credentials and experience, and not rely on advertisements and self-proclaimed expertise. All legitimate Oracle experts publish their Oracle qualifications.

Errata?  Oracle technology is changing and we strive to update our BC Oracle support information.  If you find an error or have a suggestion for improving our content, we would appreciate your feedback.  Just  e-mail:  

and include the URL for the page.


                    









Burleson Consulting

The Oracle of Database Support

Oracle Performance Tuning

Remote DBA Services


 

Copyright © 1996 -  2017

All rights reserved by Burleson

Oracle ® is the registered trademark of Oracle Corporation.

Remote Emergency Support provided by Conversational