Skip to Main Content
  • Questions
  • Using Bulk Collect and Forall Insert on a VIEW with trigger Instead Of

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Alvaro.

Asked: August 03, 2010 - 2:16 pm UTC

Last updated: August 04, 2010 - 3:20 pm UTC

Version: 10.2.0

Viewed 1000+ times

You Asked

Tom,

We have an application on Oracle Standard (so no Partition Features) and we did a virtual partition management of some tables.

The question is, if possible, how to get a forall statement (such as an insert) fired from a view bassed trigger instead of.

As I see in the trace, the forall statement is correctly fired with one execute with multiple rows, but on the real table, I see one execute per row.

How can I code the trigger in order to get on the real table one execute with multiple rows?
Are there any alternative solution for this scenario?

Here are my test tables and view, and the trace result:

Tables:
create table AVDM_TEST_BULK_01 AS SELECT * FROM user_objects WHERE ROWNUM <= 1000;
create table AVDM_TEST_BULK_02 AS SELECT * FROM user_objects WHERE ROWNUM <= 1000;


VIEW (pseudo partitioned table)
CREATE OR REPLACE VIEW AVDM_TEST_BULK AS
SELECT * FROM AVDM_TEST_BULK_01
UNION ALL 
SELECT * FROM AVDM_TEST_BULK_02
/


Trigger Instead Of
(The real one have more logic in it. This is only a sample trigger)

CREATE OR REPLACE TRIGGER AVDM_TEST_BULK_IOF
 INSTEAD OF
  INSERT OR DELETE OR UPDATE
 ON AVDM_TEST_BULK
REFERENCING NEW AS NEW OLD AS OLD
BEGIN
IF INSERTING THEN 
    INSERT INTO AVDM_TEST_BULK_01
    (
    object_name                    ,
    subobject_name                 ,
    object_id                      ,
    data_object_id                 ,
    object_type                    ,
    created                        ,
    last_ddl_time                  ,
    timestamp                      ,
    status                         ,
    temporary                      ,
    generated                      ,
    secondary                      )
    VALUES
    (
    :new.object_name                    ,
    :new.subobject_name                 ,
    :new.object_id                      ,
    :new.data_object_id                 ,
    :new.object_type                    ,
    :new.created                        ,
    :new.last_ddl_time                  ,
    :new.timestamp                      ,
    :new.status                         ,
    :new.temporary                      ,
    :new.generated                      ,
    :new.secondary                      );
END IF ;
END ;
/


Finally, the unit test code:
DECLARE
   TYPE avdm_test_bulk_aat IS TABLE OF avdm_test_bulk%ROWTYPE
      INDEX BY BINARY_INTEGER;

   l_avdm_test_bulk   avdm_test_bulk_aat;
BEGIN
   SELECT *
   BULK COLLECT INTO l_avdm_test_bulk
     FROM avdm_test_bulk;

   FORALL indx IN l_avdm_test_bulk.FIRST .. l_avdm_test_bulk.LAST
      INSERT INTO avdm_test_bulk
          values l_avdm_test_bulk (indx);

END;
/


Trace

INSERT INTO AVDM_TEST_BULK 
VALUES
 (:B1 ,:B2 ,:B3 ,:B4 ,:B5 ,:B6 ,:B7 ,:B8 ,:B9 ,:B10 ,:B11 ,:B12 ) 


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.10       0.08          0          0          0        2000
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.10       0.08          0          0          0        2000


INSERT INTO AVDM_TEST_BULK_01 ( OBJECT_NAME , SUBOBJECT_NAME , OBJECT_ID , 
  DATA_OBJECT_ID , OBJECT_TYPE , CREATED , LAST_DDL_TIME , TIMESTAMP , STATUS 
  , TEMPORARY , GENERATED , SECONDARY ) 
VALUES
 ( :B1 , :B2 , :B3 , :B4 , :B5 , :B6 , :B7 , :B8 , :B9 , :B10 , :B11 , :B12 )

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute   2000      0.02       0.04          0         29       2208        2000
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     2001      0.02       0.04          0         29       2208        2000


and Tom said...

you cannot - your "instead of trigger" is a slow by slow - row by row - the only available one you have.

You are taking the changes made to a row in a virtual table ( a view ) and procedurally programming it. It will always be "row by row".

There will be no bulk processing if you use instead of triggers - the trigger will fire once for each and ever modification made at the row level. There is no set based trigger.

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Thanks

Alvaro Varela De Marco, August 04, 2010 - 3:42 pm UTC

Tom,
Thank you for your response.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library