简体   繁体   中英

Pl/SQL Bulk Bind/ Faster Update Statements

I'm having problems using Bulk Bind in PL/SQL. Basically what I want is for a table(Component) to update its fieldvalue dependent on the Component_id and fieldname. All of these are passed in as paramaters (the type varchar2_nested_table is effectively and array of strings, one element for each update statement that needs to occur). So for instance if Component_id = 'Compid1' and fieldname = 'name' then fieldvalue should be updated to be 'new component name'.

I typed up the code below in relation to this http://www.oracle.com/technetwork/issue-archive/o14tech-plsql-l2-091157.html . The code works but is no faster than a simple loop that performs an update for every element in the IN parameters. So if the parameters have 1000 elements then 1000 update statements will be executed. I also realise I'm not using BULK COLLECT INTO but I didn't think I needed it as I don't need to select anything from the database, just update.

At the moment both take 4-5 seconds for 1000 updates. I assume I'm using the bulk bind incorrectly or have a misunderstanding of the subject as in examples I can find people are executing 50,000 rows in 2 seconds etc. From what I understand FORALL should improve performance by reducing the number of context switches. I have tried another method I found online using cursors and bulk binds but had the same outcome. Perhaps my performance expectations are too much? I don't think so from seeing others results. Any help would be greatly appreciated.

create or replace procedure BulkUpdate(sendSubject_in IN varchar2_nested_table_type,
fieldname_in IN varchar2_nested_table_type,fieldvalue_in IN   varchar2_nested_table_type) is


TYPE component_aat IS TABLE OF component.component_id%TYPE
  INDEX BY PLS_INTEGER;
TYPE fieldname_aat IS TABLE OF component.fieldname%TYPE
  INDEX BY PLS_INTEGER;
TYPE fieldvalue_aat IS TABLE OF component.fieldvalue%TYPE
  INDEX BY PLS_INTEGER;

fieldnames fieldname_aat;
fieldvalues fieldvalue_aat;
approved_components component_aat;


PROCEDURE partition_eligibility
IS
BEGIN
  FOR indx IN sendSubject_in.FIRST .. sendSubject_in.LAST
  LOOP
    approved_components(indx) := sendSubject_in(indx);
    fieldnames(indx):= fieldname_in(indx);
    fieldvalues(indx) := fieldvalue_in(indx);
  END LOOP;
END;


PROCEDURE update_components
IS
BEGIN
  FORALL indx IN approved_components.FIRST .. approved_components.LAST
    UPDATE Component
      SET Fieldvalue = fieldvalues(indx)
      WHERE Component_id = approved_components(indx)
      AND Fieldname = fieldnames(indx);
END;

BEGIN
  partition_eligibility;
  update_components;
END BulkUpdate;

Whenever, we are submitting PL/SQL blocks into oracle server always SQL statements are executed. Through SQL engine and also procedural statements are executed. Through Procedural statement executor. This procedural statement executor is available in PL/SQL engine, whenever we are using large amount of loading through SQL, PL/SQL statements always oracle server executes these statements separately through these engines.

This type of execution methodology always content switching execution methodology degrades performance of the application. To overcome this problem, oracle introduced “bulk bind” process using collections, ie in this method oracle server executes all SQL statements at a time. Bulk Collect:

Whenever we are using this clause automatically. Oracle server selecting date wise and store it into collections. Bulk collect clause used in

  1. select…into…clause
  2. cursor fetch statement
  3. DML returning clauses

Also see PL/SQL Bulk Collect And Bulk bind

There is something else going on, I suspect your individual updates are each taking a lot of time, maybe because there are triggers or inefficient indexes. (Note that if each statement is expensive individually, using bulk updates won't save you a lot of time since the context switches are negligible compared to the actual work).

Here is my test setup:

CREATE TABLE Component (
  Component_id NUMBER,
  fieldname    VARCHAR2(100),
  Fieldvalue   VARCHAR2(100),
  CONSTRAINT component_pk PRIMARY KEY (component_id, fieldname)
);

-- insert 1 million rows
INSERT INTO component 
  (SELECT ROWNUM, to_char(MOD(ROWNUM, 100)), dbms_random.string('p', 10) 
     FROM dual 
   CONNECT BY LEVEL <= 1e6);

CREATE OR REPLACE TYPE varchar2_nested_table_type AS TABLE OF VARCHAR2(100);
/

SET SERVEROUTPUT ON SIZE UNLIMITED FORMAT WRAPPED
DECLARE
   l_id    varchar2_nested_table_type;
   l_names varchar2_nested_table_type;
   l_value varchar2_nested_table_type;
   l_time  NUMBER;
BEGIN
   SELECT rownum, to_char(MOD(rownum, 100)), dbms_random.STRING('p', 10) 
     BULK COLLECT INTO l_id, l_names, l_value
     FROM dual
   CONNECT BY LEVEL <= 100000;
   l_time := dbms_utility.get_time;
   BulkUpdate(l_id, l_names, l_value);
   dbms_output.put_line((dbms_utility.get_time - l_time) || ' cs elapsed.');
END;
/

100000 rows updated in about 1.5 seconds on an unremarkable test machine. Updating the same data set row by row takes about 4 seconds.

Can you run a similar script with a newly created table?

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