Bulk collect / forall which collection type?

Hi I am trying to speed up the query below using bulk collect / forall:
SELECT h.cust_order_no AS custord, l.shipment_set AS sset
FROM info.tlp_out_messaging_hdr h, info.tlp_out_messaging_lin l
WHERE h.message_id = l.message_id
AND h.contract = '12384'
AND l.shipment_set IS NOT NULL
AND h.cust_order_no IS NOT NULL
GROUP BY h.cust_order_no, l.shipment_set
I would like to extract the 2 fields selected above into a new table as quickly as possible, but I’m fairly new to Oracle and I’m finding it difficult to sort out the best way to do it. The query below does not work (no doubt there are numerous issues) but hopefully it is sufficiently developed to shows the sort of thing I am trying to achieve:
DECLARE
TYPE xcustord IS TABLE OF info.tlp_out_messaging_hdr.cust_order_no%TYPE;
TYPE xsset IS TABLE OF info.tlp_out_messaging_lin.shipment_set%TYPE;
TYPE xarray IS TABLE OF tp_a1_tab%rowtype INDEX BY BINARY_INTEGER;
v_xarray xarray;
v_xcustord xcustord;
v_xsset xsset;
CURSOR cur IS
SELECT h.cust_order_no AS custord, l.shipment_set AS sset
FROM info.tlp_out_messaging_hdr h, info.tlp_out_messaging_lin l
WHERE h.message_id = l.message_id
AND h.contract = '1111'
AND l.shipment_set IS NOT NULL
AND h.cust_order_no IS NOT NULL;
BEGIN
OPEN cur;
LOOP
FETCH cur
BULK COLLECT INTO v_xarray LIMIT 10000;
EXIT WHEN v_xcustord.COUNT() = 0;
FORALL i IN 1 .. v_xarray.COUNT
INSERT INTO TP_A1_TAB (cust_order_no, shipment_set)
VALUES (v_xarray(i).cust_order_no,v_xarray(i).shipment_set);
commit;
END LOOP;
CLOSE cur;
END;
I am running on Oracle 9i release 2.

Well I suppose I can share the whole query as it stands for context and information (see below).
This is a very ugly piece of code that I am trying to improve. The advantage it has currently is
that it works, the disadvantage it has is that it's very slow. My thoughts were that bulk collect
and forall might be useful tools here as part of the re-write hence my original question, but perhaps not.
So on a more general note any advice on how best speed up this code would be welcome:
CREATE OR REPLACE VIEW DLP_TEST AS
WITH aa AS
(SELECT h.cust_order_no AS c_ref,l.shipment_set AS shipset,
l.line_id, h.message_id, l.message_line_no,
l.vendor_part_no AS part, l.rqst_quantity AS rqst_qty, l.quantity AS qty,
l.status AS status, h.rowversion AS allocation_date
FROM info.tlp_in_messaging_hdr h
LEFT JOIN  info.tlp_in_messaging_lin l
ON h.message_id = l.message_id
WHERE h.contract = '12384'
AND h.cust_order_no IS NOT NULL
UNION ALL
SELECT ho.cust_order_no AS c_ref, lo.shipment_set AS shipset,
lo.line_id, ho.message_id, lo.message_line_no,
lo.vendor_part_no AS part,lo.rqst_quantity AS rqst_qty, lo.quantity AS qty,
lo.status AS status, ho.rowversion AS allocation_date
FROM info.tlp_out_messaging_hdr ho, info.tlp_out_messaging_lin lo
WHERE ho.message_id = lo.message_id
AND ho.contract = '12384'
AND ho.cust_order_no IS NOT NULL),
a1 AS
(SELECT h.cust_order_no AS custord, l.shipment_set AS sset
FROM info.tlp_out_messaging_hdr h, info.tlp_out_messaging_lin l
WHERE h.message_id = l.message_id
AND h.contract = '12384'
AND l.shipment_set IS NOT NULL
AND h.cust_order_no IS NOT NULL
GROUP BY h.cust_order_no, l.shipment_set),
a2 AS
(SELECT ho.cust_order_no AS c_ref, lo.shipment_set AS shipset
FROM info.tlp_out_messaging_hdr ho, info.tlp_out_messaging_lin lo
WHERE ho.message_id = lo.message_id
AND ho.contract = '12384'
AND ho.message_type = '3B13'
AND lo.shipment_set IS NOT NULL
GROUP BY ho.cust_order_no, lo.shipment_set),
a3 AS
(SELECT a1.custord, a1.sset, CONCAT('SHIPSET',a1.sset) AS ssset
FROM a1
LEFT OUTER JOIN a2
ON a1.custord = a2.c_ref AND a1.sset = a2.shipset
WHERE a2.c_ref IS NULL),
bb AS
(SELECT so.contract, so.order_no, sr.service_request_no AS sr_no, sr.reference_no,
substr(sr.part_no,8) AS shipset,
substr(note_text,1,instr(note_text,'.',1,1)-1) AS Major_line,
note_text AS CISCO_line,ma.part_no,
(Select TO_DATE(TO_CHAR(TO_DATE(d.objversion,'YYYYMMDDHH24MISS'),'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS')
FROM ifsapp.document_text d WHERE so.note_id = d.note_id AND so.contract = '12384') AS Print_Date,
ma.note_text
FROM (ifsapp.service_request sr
LEFT OUTER JOIN ifsapp.work_order_shop_ord ws
ON sr.service_request_no = ws.wo_no
LEFT OUTER JOIN ifsapp.shop_ord so
ON ws.order_no = so.order_no)
LEFT OUTER JOIN ifsapp.customer_order co
ON sr.reference_no = co.cust_ref
LEFT OUTER JOIN ifsapp.shop_material_alloc ma
ON so.order_no = ma.order_no
JOIN a3
ON a3.custord = sr.reference_no AND a3.sset = substr(sr.part_no,8)
WHERE sr.part_contract = '12384'
AND so.contract = '12384'
AND co.contract = '12384'
AND sr.reference_no IS NOT NULL
AND ma.part_no NOT LIKE 'SHIPSET%'),
cc AS
(SELECT 
bb.reference_no,
bb.shipset,
bb.order_no,
bb.cisco_line,
aa.message_id,
aa.allocation_date,
row_number() over(PARTITION BY bb.reference_no, bb.shipset, aa.allocation_date
ORDER BY bb.reference_no, bb.shipset, aa.allocation_date, aa.message_id DESC) AS selector
FROM bb
LEFT OUTER JOIN aa
ON  bb.reference_no = aa.c_ref AND bb.shipset = aa.shipset
WHERE aa.allocation_date <= bb.print_date
OR aa.allocation_date IS NULL
OR bb.print_date IS NULL),
dd AS
(SELECT
MAX(reference_no) AS reference_no,
MAX(shipset) AS shipset,
order_no,
MAX(allocation_date) AS allocation_date
FROM cc
WHERE selector = 1                                   
GROUP BY order_no, selector),
ee AS
(SELECT
smx.order_no,
SUM(smx.qty_assigned) AS total_allocated,
SUM(smx.qty_issued) AS total_issued,
SUM(smx.qty_required) AS total_required
FROM ifsapp.shop_material_alloc smx
WHERE smx.contract = '12384'
AND smx.part_no NOT LIKE 'SHIPSET%'
GROUP BY smx.order_no),
ff AS
(SELECT
dd.reference_no,
dd.shipset,
dd.order_no,
MAX(allocation_date) AS last_allocation,
MAX(ee.total_allocated) AS total_allocated,
MAX(ee.total_issued) AS total_issued,
MAX(ee.total_required) AS total_required
FROM dd
LEFT OUTER JOIN ee
ON dd.order_no = ee.order_no
GROUP BY dd.reference_no, dd.shipset, dd.order_no),
base AS
(SELECT x.order_no, x.part_no, z.rel_no, MIN(x.dated) AS dated, MIN(y.cust_ref) AS cust_ref, MIN(z.line_no) AS line_no,
MIN(y.state) AS state, MIN(y.contract) AS contract, MIN(z.demand_order_ref1) AS demand_order_ref1
FROM ifsapp.inventory_transaction_hist x, ifsapp.customer_order y, ifsapp.customer_order_line z
WHERE x.contract = '12384'
AND x.order_no = y.order_no
AND y.order_no = z.order_no
AND x.transaction = 'REP-OESHIP'
AND x.part_no = z.part_no
AND TRUNC(x.dated) >= SYSDATE - 8
GROUP BY x.order_no, x.part_no, z.rel_no)
SELECT
DISTINCT
bb.contract,
bb.order_no,
bb.sr_no,
CAST('-' AS varchar2(40)) AS Usr,
CAST('-' AS varchar2(40)) AS Name,
CAST('01' AS number) AS Operation,
CAST('Last Reservation' AS varchar2(40)) AS Action,
CAST('-' AS varchar2(40)) AS Workcenter,
CAST('-' AS varchar2(40)) AS Next_Workcenter_no,
CAST('Print SO' AS varchar2(40)) AS Next_WC_Description,
ff.total_allocated,
ff.total_issued,
ff.total_required,
ff.shipset,
ff.last_allocation AS Action_date,
ff.reference_no
FROM ff
LEFT OUTER JOIN bb
ON bb.order_no = ff.order_no
WHERE bb.order_no IS NOT NULL
UNION ALL
SELECT
c.contract AS Site, c.order_no AS Shop_Order, b.wo_no AS SR_No,
CAST('-' AS varchar2(40)) AS UserID,
CAST('-' AS varchar2(40)) AS User_Name,
CAST('02' AS number) AS Operation,
CAST('SO Printed' AS varchar2(40)) AS Action,
CAST('SOPRINT' AS varchar2(40)) AS Workcenter,
CAST('PKRPT' AS varchar2(40)) AS Next_Workcenter_no,
CAST('Pickreport' AS varchar2(40)) AS Next_WC_Description,
CAST('0' AS number) AS Total_Allocated,
CAST('0' AS number) AS Total_Issued,
CAST('0' AS number) AS Total_Required,
e.part_no AS Ship_Set,
TO_DATE(TO_CHAR(TO_DATE(d.objversion,'YYYYMMDDHH24MISS'),'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS')AS Action_Date,
f.cust_ref AS cust_ref
FROM ifsapp.shop_ord c
LEFT OUTER JOIN ifsapp.work_order_shop_ord b
ON b.order_no = c.order_no
LEFT OUTER JOIN ifsapp.document_text d
ON d.note_id = c.note_id
LEFT OUTER JOIN ifsapp.customer_order_line e
ON e.demand_order_ref1 = TRIM(to_char(b.wo_no))
LEFT OUTER JOIN ifsapp.customer_order f
ON f.order_no = e.order_no
JOIN a3
ON a3.custord = f.cust_ref AND a3.ssset = e.part_no
WHERE c.contract = '12384'
AND e.contract = '12384'
AND d.objversion IS NOT NULL
UNION ALL
SELECT
a.site AS Site, a.order_no AS Shop_Order, b.wo_no AS SR_No, a.userid AS UserID,
a.user_name AS Name, a.operation_no AS Operation, a.action AS Action,
a.workcenter_no AS Workcenter, a.next_work_center_no AS Next_Workcenter_no,
(SELECT d.description  FROM ifsapp.work_center d WHERE a.next_work_center_no = d.work_center_no AND a.site = d.contract)
AS Next_WC_Description,
CAST('0' AS number) AS Total_Allocated,
CAST('0' AS number) AS Total_Issued,
CAST('0' AS number) AS Total_Required,
e.part_no AS Ship_set,
a.action_date AS Action_Date, f.cust_ref AS cust_ref
FROM ifsapp.shop_ord c
LEFT OUTER JOIN ifsapp.work_order_shop_ord b
ON b.order_no = c.order_no
LEFT OUTER JOIN ifsapp.customer_order_line e
ON e.demand_order_ref1 = to_char(b.wo_no)
LEFT OUTER JOIN ifsapp.customer_order f
ON f.order_no = e.order_no
LEFT OUTER JOIN info.tp_hvt_so_op_hist a
ON a.order_no = c.order_no
JOIN a3
ON a3.custord = f.cust_ref AND a3.ssset = e.part_no
WHERE a.site = '12384'
AND c.contract = '12384'
AND e.contract = '12384'
AND f.contract = '12384'
UNION ALL
SELECT so.contract AS Site, so.order_no AS Shop_Order_No, sr.service_request_no AS SR_No,
CAST('-' AS varchar2(40)) AS "User",
CAST('-' AS varchar2(40)) AS "Name",
CAST('999' AS number) AS "Operation",
CAST('Shipped' AS varchar2(40)) AS "Action",
CAST('SHIP' AS varchar2(40)) AS "Workcenter",
CAST('-' AS varchar2(40)) AS "Next_Workcenter_no",
CAST('-' AS varchar2(40)) AS "Next_WC_Description",
CAST('0' AS number) AS Total_Allocated,
CAST('0' AS number) AS Total_Issued,
CAST('0' AS number) AS Total_Required,
so.part_no AS ship_set, base.dated AS Action_Date,
sr.reference_no AS CUST_REF
FROM base
LEFT OUTER JOIN ifsapp.service_request sr
ON base.demand_order_ref1 = sr.service_request_no
LEFT OUTER JOIN ifsapp.work_order_shop_ord ws
ON sr.service_request_no = ws.wo_no
LEFT OUTER JOIN ifsapp.shop_ord so
ON ws.order_no = so.order_no
WHERE base.contract = '12384';

Similar Messages

  • Object Type and Bulk Collect/Forall

    How can I bulk collect into (and read from) a collection which is a table of an object type such as
    CREATE TABLE base_table (
         attr1 NUMBER,
         attr2 NUMBER,
         attr3 NUMBER,
         attr4 NUMBER);
    CREATE OR REPLACE TYPE rec_t IS OBJECT (
         attr1 NUMBER,
         attr2 NUMBER,
         attr3 NUMBER,
         attr4 NUMBER);
    CREATE OR REPLACE TYPE col_t IS TABLE OF rect;
    In my pl sql code I instantiate the collection type and want to populate it with a BULK COLLECT - statemt:
    PROCEDURE test IS
    v_col col_t;
    BEGIN
    SELECT rec_T(attr1, attr2, attr3, attr4)
    BULK COLLECT INTO v_col
    FROM base_table
    FORALL i IN v_col.FIRST..v_col.LAST INSERT INTO base_table VALUES (rec_t(v_col(i)));
    END;
    ? If I do it this way I get the following exception on the FORALL insert:
    PL/SQL: ORA-00947: not enough values
    Edited by: user12149927 on 22.01.2010 00:48

    try like this
    CREATE TABLE BASE_TABLE
      ATTR1  NUMBER,
      ATTR2  NUMBER,
      ATTR3  NUMBER,
      ATTR4  NUMBER
    CREATE OR REPLACE TYPE rec_t IS OBJECT (attr1 number, attr2 number, attr3 number, attr4 number);
    CREATE OR REPLACE TYPE col_t IS TABLE OF rec_t;
    CREATE OR REPLACE PROCEDURE test
    IS
       v_col   col_t;
    BEGIN
           SELECT   rec_t (attr1, attr2, attr3, attr4)
       BULK COLLECT INTO   v_col
             FROM   base_table;
       INSERT INTO base_table
          SELECT   *
            FROM   table (CAST (v_col AS col_t));
    END;Regards,
    Mahesh Kaila
    Edited by: Mahesh Kaila on Jan 22, 2010 12:56 AM

  • Issue in using Cursor+Dynamic SQL+ Bulk collect +FORALL

    Hi,
    I have a dynamic query which I need to use as a cursor to fetch records that inturn need to be inserted into a staging table.
    The issue I am facing is I am not sure how to declare the variable to fetch the records into. Since I am using a dynamic cursor how do I declare it?
    My code looks something like this -
    TYPE c_details_tbl_type IS REF CURSOR;
    c_details c_details_tbl_type;
    TYPE c_det_tbl_type IS TABLE OF c_details%ROWTYPE INDEX BY PLS_INTEGER;
    c_det_tbl c_det_tbl_type; -- ???
    BEGIN
    v_string1 := 'SELECT....'
    v_string2 := ' UNION ALL SELECT....'
    v_string3 := 'AND ....'
    v_string := v_string1||v_string2||v_string3;
    OPEN c_details FOR v_string;
    LOOP
    FETCH c_details BULK COLLECT
    INTO c_det_tbl LIMIT 1000;
    IF (c_det_tbl.COUNT > 0) THEN
              DELETE FROM STG;
              FORALL i IN 1..c_det_tbl.COUNT
              INSERT INTO STG
              VALUES (c_det_tbl(i));
    END IF;
    EXIT WHEN c_details%NOTFOUND;
    END LOOP;
    CLOSE c_details;
    END
    Thanks

    Why the bulk collect? All that this does is slow down the read process (SELECT) and write process (INSERT).
    Data selected needs (as a collection) to be pushed into the PGA memory of the PL/SQL engine. And then that very same data needs to be pushed again by the PL/SQL engine back to the database to be inserted. Why?
    It is a lot faster, needs a lot less resources, with fewer moving parts, to simply instruct the SQL engine to do both these steps using a single INSERT..SELECT statement. And this can support parallel DML too for scalability when data volumes get large.
    It is also pretty easy to make a single SQL statement like this dynamic and even support bind variables.
    Simplicity is the ultimate form of elegance. Pushing data needlessly around is not simple and thus not a very elegant way to address the problem.

  • Bulk Collect with FORALL not working - Not enough values error

    Hi,
    I am trying to copy data from one table to another which are having different number of columns. I am doing the following. But it threw not enough values error.
    Table A has more than 10 millions of records. So I am using bulk collect instead of using insert into select from.
    TABLE A (has more columns - like 25)
    c1 Number
    c2 number
    c3 varchar2
    c4 varchar2
    c25 varchar2
    TABLE B (has less columns - like 7)
    c1 Number
    c2 number
    c3 varchar2
    c4 varchar2
    c5 number
    c7 date
    c10 varchar2
    declare
    TYPE c IS REF CURSOR;
    v_c c;
    v_Sql VARCHAR2(2000);
    TYPE array is table of B%ROWTYPE;
    l_data array;
    begin
    v_Sql := 'SELECT c1, c2, c3, c4, c5, c7, c10 FROM A ORDER BY c1';
    OPEN v_c FOR v_Sql;
    LOOP
    FETCH v_c BULK COLLECT INTO ldata LIMIT 100000;
    FORALL i in 1 .. ldata.count
    INSERT
    INTO B
    VALUES ldata(i);
    END LOOP;
    COMMIT;
    exception
    WHEN OTHERS THEN
    ROLLBACK;
    dbms_output.put_line('Exception Occurred' || SQLERRM);
    END;
    When I execute this, I am getting
    PL/SQL: ORA-00947: not enough values
    Any suggestions please. Thanks in advance.

    Table A has more than 10 millions of records. So I am using bulk collect instead of using insert into select from.That doesn't make sense to me. An INSERT ... SELECT is going to be more efficient, more maintainable, easier to write, and easier to understand.
    INSERT INTO b( c1, c2, c3, c4, c5, c7, c10 )
      SELECT c1, c2, c3, c4, c5, c7, c10
        FROM a;is going to be faster, use fewer resources, be far less error-prone, and have a far more obvious purpose when some maintenance programmer comes along than any PL/SQL block that does the same thing.
    If you insist on using PL/SQL, what version of Oracle are you using? You should be able to do something like
    DECLARE
      TYPE b_tbl IS TABLE OF b%rowtype;
      l_array b_tbl;
      CURSOR a_cursor
          IS SELECT c1, c2, c3, c4, c5, c7, c10 FROM A;
    BEGIN
      OPEN a_cursor;
      LOOP
        FETCH a_cursor
         BULK COLLECT INTO l_array
        LIMIT 10000;
        EXIT WHEN l_array.COUNT = 0;
        FORALL i IN l_array.FIRST .. l_array.LAST
          INSERT INTO b
            VALUES l_array(i);
      END LOOP;
      COMMIT;
    END;That at least eliminates the infinite loop and the unnecessary dynamic SQL. If you're using older versions of Oracle (it's always helpful to post that information up front), the code may need to be a bit more complex.
    Justin
    Edited by: Justin Cave on Jan 19, 2011 5:46 PM

  • How to use Bulk Collect and Forall

    Hi all,
    We are on Oracle 10g. I have a requirement to read from table A and then for each record in table A, find matching rows in table B and then write the identified information in table B to the target table (table C). In the past, I had used two ‘cursor for loops’ to achieve that. To make the new procedure, more efficient, I would like to learn to use ‘bulk collect’ and ‘forall’.
    Here is what I have so far:
    DECLARE
    TYPE employee_array IS TABLE OF EMPLOYEES%ROWTYPE;
    employee_data  employee_array;
    TYPE job_history_array IS TABLE OF JOB_HISTORY%ROWTYPE;
    Job_history_data   job_history_array;
    BatchSize CONSTANT POSITIVE := 5;
    -- Read from File A
    CURSOR c_get_employees IS
             SELECT  Employee_id,
                       first_name,
                       last_name,
                       hire_date,
                       job_id
              FROM EMPLOYEES;
    -- Read from File B based on employee ID in File A
    CURSOR c_get_job_history (p_employee_id number) IS
             select start_date,
                      end_date,
                      job_id,
                      department_id
             FROM JOB_HISTORY
             WHERE employee_id = p_employee_id;
    BEGIN
        OPEN c_get_employees;
        LOOP
            FETCH c_get_employees BULK COLLECT INTO employee_data.employee_id.LAST,
                                                                              employee_data.first_name.LAST,
                                                                              employee_data.last_name.LAST,
                                                                              employee_data.hire_date.LAST,
                                                                              employee_data.job_id.LAST
             LIMIT BatchSize;
            FORALL i in 1.. employee_data.COUNT
                    Open c_get_job_history (employee_data(i).employee_id);
                    FETCH c_get_job_history BULKCOLLECT INTO job_history_array LIMIT BatchSize;
                             FORALL k in 1.. Job_history_data.COUNT LOOP
                                            -- insert into FILE C
                                              INSERT INTO MY_TEST(employee_id, first_name, last_name, hire_date, job_id)
                                                                values (job_history_array(k).employee_id, job_history_array(k).first_name,
                                                                          job_history_array(k).last_name, job_history_array(k).hire_date,
                                                                          job_history_array(k).job_id);
                                             EXIT WHEN job_ history_data.count < BatchSize                        
                             END LOOP;                          
                             CLOSE c_get_job_history;                          
                     EXIT WHEN employee_data.COUNT < BatchSize;
           END LOOP;
            COMMIT;
            CLOSE c_get_employees;
    END;
                     When I run this script, I get
    [Error] Execution (47: 17): ORA-06550: line 47, column 17:
    PLS-00103: Encountered the symbol "OPEN" when expecting one of the following:
       . ( * @ % & - + / at mod remainder rem select update with
       <an exponent (**)> delete insert || execute multiset save
       merge
    ORA-06550: line 48, column 17:
    PLS-00103: Encountered the symbol "FETCH" when expecting one of the following:
       begin function package pragma procedure subtype type use
       <an identifier> <a double-quoted delimited-identifier> form
       current cursorWhat is the best way to code this? Once, I learn how to do this, I apply the knowledge to the real application in which file A would have around 200 rows and file B would have hundreds of thousands of rows.
    Thank you for your guidance,
    Seyed

    Hello BlueShadow,
    Following your advice, I modified a stored procedure that initially was using two cursor for loops to read from tables A and B to write to table C to use instead something like your suggestion listed below:
    INSERT INTO tableC
    SELECT …
    FROM tableA JOIN tableB on (join condition).I tried this change on a procedure writing to tableC with keys disabled. I will try this against the real table that has primary key and indexes and report the result later.
    Thank you very much,
    Seyed

  • Having problem using BULK COLLECT - FORALL

    Hi,
    I'm facing a problem while setting a table type value before inserting into a table using FORALL.
    My concern is that i'm unable to generate the values in FOR LOOP, as by using dbms_output.put_line i observed that after 100 rows execution the process exits giving error as
    ORA-22160: element at index [1] does not exist
    ORA-06512: at "XYZ", line 568
    ORA-06512: at line 2
    I need to use the values stored in FOR LOOP in the same order for insertion in table TEMP using FOR ALL;
    I'm guessing that i'm using the wrong technique for storing values in FOR LOOP.
    Below given is my SP structure.
    Any suggestion would be hepful.
    Thanks!!
    create or replace procedure XYZ
    cursor cur is
    select A,B,C,D from ABCD; ---expecting 40,000 row fetch
    type t_A is table of ABCD.A%type index by pls_integer;
    type t_B is table of ABCD.B%type index by pls_integer;
    type t_C is table of ABCD.C%type index by pls_integer;
    type t_D is table of ABCD.D%type index by pls_integer;
    v_A t_A;
    v_B t_B;
    v_C t_C;
    v_D t_D;
    type t_E is table of VARCHAR2(100);
    type t_F is table of VARCHAR2(100);
    v_E t_E := t_E();
    v_F t_F := t_F();
    begin
    open cur;
    loop
    fetch cur BULK COLLECT INTO v_A,v_B,v_C,v_D limit 100;
    for i in 1 .. v_A.count loop
    v_E.extend(i);
    select 1111 into v_E(i) from dual;
    v_F.extend(i);
    v_F(i) := 'Hi !!';
    ----calculating v_E(i) and v_F(i) here----
    end loop;
    forall in i in 1 .. v_A.count
    insert into table TEMP values (v_E(i), v_F(i));
    exit when cur%NOTFOUND;
    end loop;
    close cur;
    end;
    BANNER
    Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
    PL/SQL Release 10.2.0.4.0 - Production
    CORE     10.2.0.4.0     Production
    TNS for HPUX: Version 10.2.0.4.0 - Production
    NLSRTL Version 10.2.0.4.0 - Production
    -------

    The problem is that inside the IF ELSIF blocks i need to query various tables..As I thought. But why did you concentrate on BULK COLLECT - FORALL?
    The cursor whereas does take more time to execute.More time then?
    We have join of two tables have 18,00,000(normal table) and >17,92,2067(MView) records, having inidex on one the join column.
    After joining these two and adding the filter conditions i'm having around >40,000 >rows.? You have a cursor row. And then inside the loop you have a query which returns 40'000 rows? What do you do with that data?
    Is the query you show running INSIDE the loop?
    I guess you still talk about the LOOP query and your are unhappy that it is not taking an index?
    1. The loop is NOT the problem. It's the "... i need to query various tables"
    2. ORACLE is ok when it's NOT taking the index. That is faster!!
    3. If you add code and execution plans, please add tags. Otherwise it's unreadable.
    Try to merge your LOOP query with the "various tables" and make ONE query out of 40000*various ;-)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               

  • BULK COLLECT and FORALL with dynamic INSERT.

    Hello,
    I want to apply BULK COLLECT and FORALL feature for a insert statement in my procedure for performance improvements as it has to insert a huge amount of data.
    But the problem is that the insert statement gets generated dynamically and even the table name is found at the run-time ... so i am not able to apply the performance tuning concepts.
    See below the code
    PROCEDURE STP_MES_INSERT_GLOBAL_TO_MAIN
      (P_IN_SRC_TABLE_NAME                  VARCHAR2 ,
      P_IN_TRG_TABLE_NAME                  VARCHAR2 ,
      P_IN_ED_TRIG_ALARM_ID                 NUMBER ,
      P_IN_ED_CATG_ID                 NUMBER ,
      P_IN_IS_PIECEID_ALARM IN CHAR,
      P_IN_IS_LAST_RECORD IN CHAR
    IS
          V_START_DATA_ID                 NUMBER;
          V_STOP_DATA_ID                   NUMBER;
          V_FROM_DATA_ID                   NUMBER;
          V_TO_DATA_ID                       NUMBER;
          V_MAX_REC_IN_LOOP              NUMBER := 30000;
          V_QRY1         VARCHAR2(32767);
    BEGIN
                EXECUTE IMMEDIATE 'SELECT MIN(ED_DATA_ID), MAX(ED_DATA_ID) FROM '|| P_IN_SRC_TABLE_NAME INTO V_START_DATA_ID , V_STOP_DATA_ID;
                --DBMS_OUTPUT.PUT_LINE('ORIGINAL START ID := '||V_START_DATA_ID ||' ORIGINAL  STOP ID := ' || V_STOP_DATA_ID);
                V_FROM_DATA_ID := V_START_DATA_ID ;
                IF (V_STOP_DATA_ID - V_START_DATA_ID ) > V_MAX_REC_IN_LOOP THEN
                        V_TO_DATA_ID := V_START_DATA_ID + V_MAX_REC_IN_LOOP;
                ELSE
                       V_TO_DATA_ID := V_STOP_DATA_ID;
                END IF;
              LOOP
                    BEGIN
                 LOOP      
                            V_QRY1 := ' INSERT INTO '||P_IN_TRG_TABLE_NAME||
                            ' SELECT * FROM '||P_IN_SRC_TABLE_NAME ||
                            ' WHERE ED_DATA_ID BETWEEN ' || V_FROM_DATA_ID ||' AND ' || V_TO_DATA_ID;
                    EXECUTE IMMEDIATE V_QRY1;
    commit;
                                     V_FROM_DATA_ID :=  V_TO_DATA_ID + 1;
                            IF  ( V_STOP_DATA_ID - V_TO_DATA_ID > V_MAX_REC_IN_LOOP ) THEN
                                  V_TO_DATA_ID := V_TO_DATA_ID + V_MAX_REC_IN_LOOP;
                            ELSE
                                  V_TO_DATA_ID := V_TO_DATA_ID + (V_STOP_DATA_ID - V_TO_DATA_ID);
                            END IF;
                    EXCEPTION
                             WHEN OTHERS THEN.............
    ....................so on Now you can observer here that P_IN_SRC_TABLE_NAME is the source table name which we get as a parameter at run-time. I have used 2 table in the insert statement P_IN_TRG_TABLE_NAME (in which i have to insert data) and P_IN_SRC_TABLE_NAME(from where i have to insert data)
      V_QRY1 := ' INSERT INTO '||P_IN_TRG_TABLE_NAME||
                            ' SELECT * FROM '||P_IN_SRC_TABLE_NAME ||
                            ' WHERE ED_DATA_ID BETWEEN ' || V_FROM_DATA_ID ||' AND ' || V_TO_DATA_ID;
                    EXECUTE IMMEDIATE V_QRY1;now when i appy the bulk collect and forall feature i am facing the out of scope problem....see the code below ...
    BEGIN
                EXECUTE IMMEDIATE 'SELECT MIN(ED_DATA_ID), MAX(ED_DATA_ID) FROM '|| P_IN_SRC_TABLE_NAME INTO V_START_DATA_ID , V_STOP_DATA_ID;
                --DBMS_OUTPUT.PUT_LINE('ORIGINAL START ID := '||V_START_DATA_ID ||' ORIGINAL  STOP ID := ' || V_STOP_DATA_ID);
                V_FROM_DATA_ID := V_START_DATA_ID ;
                IF (V_STOP_DATA_ID - V_START_DATA_ID ) > V_MAX_REC_IN_LOOP THEN
                        V_TO_DATA_ID := V_START_DATA_ID + V_MAX_REC_IN_LOOP;
                ELSE
                       V_TO_DATA_ID := V_STOP_DATA_ID;
                END IF;
              LOOP
                    DECLARE
                     TYPE TRG_TABLE_TYPE IS TABLE OF P_IN_SRC_TABLE_NAME%ROWTYPE;
                     V_TRG_TABLE_TYPE TRG_TABLE_TYPE;
                     CURSOR TRG_TAB_CUR IS
                     SELECT * FROM P_IN_SRC_TABLE_NAME
                     WHERE ED_DATA_ID BETWEEN V_FROM_DATA_ID AND V_TO_DATA_ID;
                     V_QRY1 varchar2(32767);
                    BEGIN
                    OPEN TRG_TAB_CUR;
                    LOOP
                    FETCH TRG_TAB_CUR BULK COLLECT INTO V_TRG_TABLE_TYPE LIMIT 30000;
                    FORALL I IN 1..V_TRG_TABLE_TYPE.COUNT
                    V_QRY1 := ' INSERT INTO '||P_IN_TRG_TABLE_NAME||' VALUES V_TRG_TABLE_TYPE(I);'
                    EXECUTE IMMEDIATE V_QRY1;
                    EXIT WHEN TRG_TAB_CUR%NOTFOUND;
                    END LOOP;
                    CLOSE TRG_TAB_CUR;
                            V_FROM_DATA_ID :=  V_TO_DATA_ID + 1;
                            IF  ( V_STOP_DATA_ID - V_TO_DATA_ID > V_MAX_REC_IN_LOOP ) THEN
                                  V_TO_DATA_ID := V_TO_DATA_ID + V_MAX_REC_IN_LOOP;
                            ELSE
                                  V_TO_DATA_ID := V_TO_DATA_ID + (V_STOP_DATA_ID - V_TO_DATA_ID);
                            END IF;
                    EXCEPTION
                             WHEN OTHERS THEN.........so on
    But the above code is not helping me ,  what i am doing wrong ??? how can i tune this dynamically generated statement to use bulk collect for better performace ......
    Thanks in Advance !!!!

    Hello,
    a table name cannot be bind as a parameter in SQL, this wont't compile:
    EXECUTE IMMEDIATE ' INSERT INTO :1 VALUES ......
    USING P_IN_TRG_TABLE_NAME ...but this should work:
    EXECUTE IMMEDIATE ' INSERT INTO ' || P_IN_TRG_TABLE_NAME || ' VALUES ......You cannot declare a type that is based on a table which name is in a variable.
    PL/SQL is stronly typed language, a type must be known at compile time, a code like this is not allowed:
    PROCEDURE xx( src_table_name varchar2 )
    DECLARE
       TYPE tab IS TABLE OF src_table_name%ROWTYPE;
      ...This can be done by creating one big dynamic SQL - see example below (tested on Oracle 10 XE - this is a slightly simplified version of your procedure):
    CREATE OR REPLACE
    PROCEDURE stp1(
      p_in_src_table_name                  VARCHAR2 ,
      p_in_trg_table_name                  VARCHAR2 ,
      v_from_data_id     NUMBER := 100,
      v_to_data_id       NUMBER := 100000
    IS
    BEGIN
      EXECUTE IMMEDIATE q'{
      DECLARE
         TYPE trg_table_type IS TABLE OF }' || p_in_src_table_name || q'{%ROWTYPE;
         V_TRG_TABLE_TYPE TRG_TABLE_TYPE;
         CURSOR TRG_TAB_CUR IS
         SELECT * FROM }' || p_in_src_table_name ||
         q'{ WHERE ED_DATA_ID BETWEEN :V_FROM_DATA_ID AND :V_TO_DATA_ID;
      BEGIN
          OPEN TRG_TAB_CUR;
          LOOP
                FETCH TRG_TAB_CUR BULK COLLECT INTO V_TRG_TABLE_TYPE LIMIT 30000;
                FORALL I IN 1 .. V_TRG_TABLE_TYPE.COUNT
                INSERT INTO }' || p_in_trg_table_name || q'{ VALUES V_TRG_TABLE_TYPE( I );
                EXIT WHEN TRG_TAB_CUR%NOTFOUND;
          END LOOP;
          CLOSE TRG_TAB_CUR;
      END; }'
      USING v_from_data_id, v_to_data_id;
      COMMIT;
    END;But this probably won't give any performace improvements. Bulk collect and forall can give performance improvements when there is a DML operation inside a loop,
    and this one single DML operates on only one record or relatively small number of records, and this DML is repeated many many times in the loop.
    I guess that your code is opposite to this - it contains insert statement that operates on many records (one single insert ~ 30000 records),
    and you are trying to replace it with bulk collect/forall - INSERT INTO ... SELECT FROM will almost alwayst be faster than bulk collect/forall.
    Look at simple test - below is a procedure that uses INSERT ... SELECT :
    CREATE OR REPLACE
    PROCEDURE stp(
      p_in_src_table_name                  VARCHAR2 ,
      p_in_trg_table_name                  VARCHAR2 ,
      v_from_data_id     NUMBER := 100,
      v_to_data_id       NUMBER := 100000
    IS
    V_QRY1 VARCHAR2(32767);
    BEGIN
          V_QRY1 := ' INSERT INTO '||   P_IN_TRG_TABLE_NAME ||
                    ' SELECT * FROM '|| P_IN_SRC_TABLE_NAME ||
                    ' WHERE ed_data_id BETWEEN :f AND :t ';
          EXECUTE IMMEDIATE V_QRY1
          USING V_FROM_DATA_ID, V_TO_DATA_ID;
          COMMIT;
    END;
    /and we can compare both procedures:
    SQL> CREATE TABLE test333
      2  AS SELECT level ed_data_id ,
      3            'XXX ' || LEVEL x,
      4            'YYY ' || 2 * LEVEL y
      5  FROM dual
      6  CONNECT BY LEVEL <= 1000000;
    Table created.
    SQL> CREATE TABLE test333_dst AS
      2  SELECT * FROM test333 WHERE 1 = 0;
    Table created.
    SQL> set timing on
    SQL> ed
    Wrote file afiedt.buf
      1  BEGIN
      2     FOR i IN 1 .. 100 LOOP
      3        stp1( 'test333', 'test333_dst', 1000, 31000 );
      4     END LOOP;
      5* END;
    SQL> /
    PL/SQL procedure successfully completed.
    Elapsed: 00:00:22.12
    SQL> ed
    Wrote file afiedt.buf
      1  BEGIN
      2     FOR i IN 1 .. 100 LOOP
      3        stp( 'test333', 'test333_dst', 1000, 31000 );
      4     END LOOP;
      5* END;
    SQL> /
    PL/SQL procedure successfully completed.
    Elapsed: 00:00:14.86without bulk collect ~ 15 sec.
    bulk collect version ~ 22 sec. .... 7 sec longer / 15 sec. = about 45% performance decrease.

  • Where to put the commit in the FORALL BULK COLLECT LOOP

    Hi,
    Have the following LOOP code using FORALL and bulk collect, but didnt know where to put the
    'commit' :
    open f_viewed;
    LOOP
    fetch f_viewed bulk collect into f_viewed_rec LIMIT 2000;
    forall i in 1..f_viewed_rec.count
    insert into jwoodman.jw_job_history_112300
    values f_viewed_rec(i);
    --commit; [Can I put this 'commit' here? - Jenny]
    EXIT when f_viewed%NOTFOUND;
    END LOOP;
    commit;
    Thanks,
    - Jenny

    mc**** wrote:
    Bulk collect normally used with large data sets. If you have less dataset such as 1000-2000 records then you canot get such a performance improvent using bulk collect.(Please see oracle documents for this)
    When you update records Oracle acquire exclusive lock for that. So if you use commit inside the loop then it will process number of records defined by limit parameter at ones and then commit those changes.
    That will release all locks acquired by Oracle and also teh memory used to keep those uncommited transactions.
    If you use commit outside the loop,
    Just assume that you insert 100,000 records, all those records will store in oracle memory and it will affect all other users performance as well.
    Further more if you update 100,000 records then it will hold exclusive lock for all 100,000 records addtion to the usage of the oracle memory.
    I am using this for telco application which we process over 30 million complex records (one row has 234 columns).
    When we work with large data sets we do not depends with the oracle basic rollback function. because when you keep records without commit itb uses oracle memory and badly slowdown all other processes.Hi mc****,
    What a load of dangerous and inaccurate rubbish to be telling a new Oracle developer. Commit processing should be driven by the logical unit of a transaction. This should hold true whether that transaction involves a few rows or millions. If, and only if, the transaction is so large that it affects the size constraints of the database resources, in particular, rollback or redo space, then you can consider breaking that transaction up to smaller transactions.
    Why is frequent committing undesirable I hear you ask?
    First of all it is hugely wasteful of rollback or redo space. This is because while the database is capable of locking at a row level, redo is written at a block level, which means that if you update, delete or insert a million rows and commit after each individual statement, then that is a million blocks that need to go into redo. As many of these rows will be in the same block, if you instead do these as one transaction, then the same block in redo can be transacted upon, making the operation more efficient. True, locks will be held for longer, but if this is new data being done in batches then users will rarely be inconvenienced. If locking is a problem then I would suggest that you should be looking at how you are doing your processing.
    Secondly, committing brings into play one of the major serialization points in the database, log sync. When a transaction is committed, the log buffer needs to be written to disc. This occurs serially for multiple commits. Each commit has to wait until the commit before has completed. This becomes even more of a bottleneck if you are using Data Guard in SYNC mode, as the commit cycle does not complete until the remote log is notified as written.
    This then brings us two rules of thumb that will always lead a developer in the right direction.
    1. Commit as infrequently as possible, usually at the logical unit of a transaction
    2. When building transactions, first of all seek to do it using straight SQL (CTAS, insert select, update where etc). If this can't be easily achieved, then use PL/SQL bulk operations.
    Regards
    Andre

  • Calling Stored procedure which uses Bulk Collect

    Hi All, I have Oracle stored procedure which uses Bulk Collect and returns table type parameter as output. Can anyone please help me how Can I call this kind of stored procedures which returns table type output using VB and Oracle's Driver. (I am successfully able to call using MS ODBC driver, but I want to use OraOLEDB driver.)

    861412 wrote:
    how Can I call this kind of stored procedures which returns table type output using VB and Oracle's Driver. This forum deals with the server-side languages SQL and PL/SQL.
    Your question deals with the client side and Visual Basic language.

  • USING IF IN FORALL AND BULK COLLECT

    Hi All,
    I wrote an program..I have doubt whether i can use if condition in FORALL INSERT OR BULK COLLECT? I can't go for 'for loop' ....Is there any way to to do validations in FORALL INSERT and BULK COLLECT like we do in 'for loop' ...
    create or replace
    PROCEDURE name AS
    CURSOR CUR_name IS
    SELECT OLD_name,NEW_name FROM DIRECTORY_LISTING_AUDIT;
    TYPE V_OLD_name IS TABLE OF DIRECTORY_LISTING_AUDIT.OLD_name%TYPE;
    Z_V_OLD_name V_OLD_name ;
    TYPE V_NEW_name IS TABLE OF DIRECTORY_LISTING_AUDIT.NEW_name%TYPE;
    Z_V_NEW_name V_NEW_name ;
    BEGIN
    OPEN CUR_name ;
    LOOP
    FETCH CUR_name BULK COLLECT INTO Z_V_OLD_name,Z_V_NEW_name;
    IF Z_V_NEW_name <> NULL THEN
    Z_V_OLD_name := Z_V_NEW_name ;
    Z_V_NEW_name := NULL;
    END IF;
    FORALL I IN Z_V_NEW_name.COUNT
    INSERT INTO TEMP_DIREC_AUDIT (OLD_name,NEW_name) VALUES (Z_V_OLD_name(I),Z_V_NEW_name(I));
    EXIT WHEN CUR_name%NOTFOUND;
    END LOOP;
    CLOSE CUR_name;
    END name;

    FORALL i IN v_tab.FIRST .. v_tab.LAST
             INSERT ALL
                WHEN v_tab (i) = 1
                THEN
                   INTO sen_temp
                        (col_num
                 VALUES (v_tab (i) + 5
                SELECT dummy
                  FROM DUAL;
          EXIT WHEN c1%NOTFOUND;this is the one u looking for i guess...

  • How to use BULK COLLECT, FORALL and TREAT

    There is a need to read match and update data from and into a custom table. The table would have about 3 millions rows and holds key numbers. BAsed on a field value of this custom table, relevant data needs to be fetched from joins of other tables and updated in the custom table. I plan to use BULK COLLECT and FORALL.
    All examples I have seen, do an insert into a table. How do I go about reading all values of a given field and fetching other relevant data and then updating the custom table with data fetched.
    Defined an object with specifics like this
    CREATE OR REPLACE TYPE imei_ot AS OBJECT (
    recid NUMBER,
    imei VARCHAR2(30),
    STORE VARCHAR2(100),
    status VARCHAR2(1),
    TIMESTAMP DATE,
    order_number VARCHAR2(30),
    order_type VARCHAR2(30),
    sku VARCHAR2(30),
    order_date DATE,
    attribute1 VARCHAR2(240),
    market VARCHAR2(240),
    processed_flag VARCHAR2(1),
    last_update_date DATE
    Now within a package procedure I have defined like this.
    type imei_ott is table of imei_ot;
    imei_ntt imei_ott;
    begin
    SELECT imei_ot (recid,
    imei,
    STORE,
    status,
    TIMESTAMP,
    order_number,
    order_type,
    sku,
    order_date,
    attribute1,
    market,
    processed_flag,
    last_update_date
    BULK COLLECT INTO imei_ntt
    FROM (SELECT stg.recid, stg.imei, cip.store_location, 'S',
    co.rtl_txn_timestamp, co.rtl_order_number, 'CUST',
    msi.segment1 || '.' || msi.segment3,
    TRUNC (co.txn_timestamp), col.part_number, 'ZZ',
    stg.processed_flag, SYSDATE
    FROM custom_orders co,
    custom_order_lines col,
    custom_stg stg,
    mtl_system_items_b msi
    WHERE co.header_id = col.header_id
    AND msi.inventory_item_id = col.inventory_item_id
    AND msi.organization_id =
    (SELECT organization_id
    FROM hr_all_organization_units_tl
    WHERE NAME = 'Item Master'
    AND source_lang = USERENV ('LANG'))
    AND stg.imei = col.serial_number
    AND stg.processed_flag = 'U');
    /* Update staging table in one go for COR order data */
    FORALL indx IN 1 .. imei_ntt.COUNT
    UPDATE custom_stg
    SET STORE = TREAT (imei_ntt (indx) AS imei_ot).STORE,
    status = TREAT (imei_ntt (indx) AS imei_ot).status,
    TIMESTAMP = TREAT (imei_ntt (indx) AS imei_ot).TIMESTAMP,
    order_number = TREAT (imei_ntt (indx) AS imei_ot).order_number,
    order_type = TREAT (imei_ntt (indx) AS imei_ot).order_type,
    sku = TREAT (imei_ntt (indx) AS imei_ot).sku,
    order_date = TREAT (imei_ntt (indx) AS imei_ot).order_date,
    attribute1 = TREAT (imei_ntt (indx) AS imei_ot).attribute1,
    market = TREAT (imei_ntt (indx) AS imei_ot).market,
    processed_flag =
    TREAT (imei_ntt (indx) AS imei_ot).processed_flag,
    last_update_date =
    TREAT (imei_ntt (indx) AS imei_ot).last_update_date
    WHERE recid = TREAT (imei_ntt (indx) AS imei_ot).recid
    AND imei = TREAT (imei_ntt (indx) AS imei_ot).imei;
    DBMS_OUTPUT.put_line ( TO_CHAR (SQL%ROWCOUNT)
    || ' rows updated using Bulk Collect / For All.'
    EXCEPTION
    WHEN NO_DATA_FOUND
    THEN
    DBMS_OUTPUT.put_line ('No Data: ' || SQLERRM);
    WHEN OTHERS
    THEN
    DBMS_OUTPUT.put_line ('Other Error: ' || SQLERRM);
    END;
    Now for the unfortunate part. When I compile the pkg, I face an error
    PL/SQL: ORA-00904: "LAST_UPDATE_DATE": invalid identifier
    I am not sure where I am wrong. Object type has the last update date field and the custom table also has the same field.
    Could someone please throw some light and suggestion?
    Thanks
    uds

    I suspect your error comes from the »bulk collect into« and not from the »forall loop«.
    From a first glance you need to alias sysdate with last_update_date and some of the other select items need to be aliased as well :
    But a simplified version would be
    select imei_ot (stg.recid,
                    stg.imei,
                    cip.store_location,
                    'S',
                    co.rtl_txn_timestamp,
                    co.rtl_order_number,
                    'CUST',
                    msi.segment1 || '.' || msi.segment3,
                    trunc (co.txn_timestamp),
                    col.part_number,
                    'ZZ',
                    stg.processed_flag,
                    sysdate
    bulk collect into imei_ntt
      from custom_orders co,
           custom_order_lines col,
           custom_stg stg,
           mtl_system_items_b msi
    where co.header_id = col.header_id
       and msi.inventory_item_id = col.inventory_item_id
       and msi.organization_id =
                  (select organization_id
                     from hr_all_organization_units_tl
                    where name = 'Item Master' and source_lang = userenv ('LANG'))
       and stg.imei = col.serial_number
       and stg.processed_flag = 'U';
    ...

  • (How) Can a SP call the Function which has BULK COLLECT return?

    I have a function using BULL COLLECT to put (XML data) many line into a table type record. (I had this as a function because it's gong to call many times).
    From one SP program, I will need to call it to get converted XML data info, how should i call it? If it's single line return, I could use
    my_result := get_tbl_xml(p_xml);
    but when it's table type, even I defined it, it still get error. Here is the program:
    Declare (at package spec)
    TYPE OrderDtl_Tab IS TABLE OF xxx%ROWTYPE INDEX BY BINARY_INTEGER;
    in_OrderDtlTab OrderDtl_Tab;
    FUNCTION get_tbl_xml (p_xml IN CLOB ) RETURN OrderDtl_Tab IS
    BEGIN
    SELECT extractvalue(VALUE(x), '/xxxx/xxxx') AS xxxx BULK COLLECT
    INTO in_OrderDtlTab
    FROM TABLE(xmlsequence(extract(xmltype(p_xml), '/Order/Lines/*'))) x;
    RETURN in_OrderDtlTab;
    END;
    PROCEDURE SAVE_A (p_xml IN CLOB )
    BEGIN
    --- how to call get_tbl_xml to get result, so I will be able to insert into DB table
    in_OrderDtlTab := get_tbl_xml(p_xml);
    END;
    --------------------------------------------------------------------------------------------------------------------

    May be this is better
    FUNCTION get_tbl_xml (p_xml IN CLOB ) RETURN OrderDtl_Tab IS
    BEGIN
    SELECT extractvalue(VALUE(x), '/xxxx/xxxx') AS xxxx BULK COLLECT
    INTO in_OrderDtlTab
    FROM TABLE(xmlsequence(extract(xmltype(p_xml), '/Order/Lines/*'))) x;
    RETURN in_OrderDtlTab;
    END;
    PROCEDURE SAVE_A (p_xml IN CLOB )
    yourrow your_table%rowtype;
    BEGIN
    --- how to call get_tbl_xml to get result, so I will be able to insert into DB table
    in_OrderDtlTab := get_tbl_xml(p_xml);
    FORALL myrowtype
    IN in_OrderDtlTab .FIRST .. in_OrderDtlTab .LAST
    INSERT INTO Your_table
    VALUES in_OrderDtlTab(yourrow);
    END;
    END;

  • How to bypass FORALL in SELECT .. BULK COLLECT

    Using Oracle 8.1.7
    I developed a stored procedure LOT
    TYPE TIds IS Table of Integer;
    TYPE TFloats IS Table of Float;
    procedure LOT(Ids TIds) ....
    is
    Prices TFloats;
    Quantities TFloats;
    begin
    for I in 1..Ids.Count loop
    SELECT ...
    BULK COLLECT INTO Prices, Quantities, ...
    WHERE ... = Ids(I)
    end loop;
    end;
    For each Id from IDS collection my procedure SELECTs several
    rows (avg. 2 or 3 rows). IDS contains about 1000 - 10000 items.
    Productivity is very low.
    Because is the many context switches
    between SQL engine and PL/SQL engine.
    The point is to collect all data by single 'huge' SELECT instead
    of numerous 'short' SELECTs.
    I. e. I need somethng like
    FORALL I in 1..Ids.Count
    SELECT ...
    BULK COLLECT INTO Prices, Quantities, ...
    WHERE ... = Ids(I)
    or like this
    FORALL I in 1..Ids.Count
    SELECT ...
    WHERE ... = Ids(I) RETURNING ... BULK COLLECT INTO ...
    I know it's invalid syntax because PL/SQL doesn't support BULK
    COLLECT clause for SELECT in FORALL loop.
    How should I bypass the problem?
    Is a proper way to use temporary table as storehouse for IDS collection?
    Something else?
    Regards,
    Piter Elagin (AlienZzzz)

    Hi Ivan,
       the problem is that the port is configurable at installation time.  On a NetWeaver 04 portal, I believe the default was 50000.  That should get you to one of the servers.  You may try 50100, 50200, etc. 
    You can also ask who ever did the installation.  the other place to look is the webdispatcher configuration file.  It would have to know the server and port to dispatch to.
    Hope that helps.
    John

  • ORA-06502 during a procedure which uses Bulk collect feature and nested tab

    Hello Friends,
    have created one procedure which uses Bulk collect and nested table to hold the bulk data. This procedure was using one cursor and a nested table with the same type as the cursor to hold data fetched from cursor. Bulk collection technique was used to collect data from cursor to nested table. But it is giving ORA-06502 error.
    I reduced code of procedure to following to trace the error point. But still error is comming. Please help us to find the cause and solve it.
    Script which is giving error:
    declare
    v_Errorflag BINARY_INTEGER;
    v_flag number := 1;
    CURSOR cur_terminal_info Is
    SELECT distinct
    'a' SettlementType
    FROM
    dual;
    TYPE typ_cur_terminal IS TABLE OF cur_terminal_info%ROWTYPE;
    Tab_Terminal_info typ_cur_Terminal;
    BEGIN
    v_Errorflag := 2;
    OPEN cur_terminal_info;
    LOOP
    v_Errorflag := 4;
    FETCH cur_terminal_info BULK COLLECT INTO tab_terminal_info LIMIT 300;
    EXIT WHEN cur_terminal_info%rowcount &lt;= 0;
    v_Errorflag := 5;
    FOR Y IN Tab_Terminal_Info.FIRST..tab_terminal_info.LAST
    LOOP
    dbms_output.put_line(v_flag);
    v_flag := v_flag + 1;
    end loop;
    END LOOP;
    v_Errorflag := 13;
    COMMIT;
    END;
    I have updated script as following to change datatype as varchar2 for nested table, but still same error is
    comming..
    declare
    v_Errorflag BINARY_INTEGER;
    v_flag number := 1;
    CURSOR cur_terminal_info Is
    SELECT distinct
    'a' SettlementType
    FROM
    dual;
    TYPE typ_cur_terminal IS TABLE OF varchar2(50);
    Tab_Terminal_info typ_cur_Terminal;
    BEGIN
    v_Errorflag := 2;
    OPEN cur_terminal_info;
    LOOP
    v_Errorflag := 4;
    FETCH cur_terminal_info BULK COLLECT INTO tab_terminal_info LIMIT 300;
    EXIT WHEN cur_terminal_info%rowcount &lt;= 0;
    v_Errorflag := 5;
    FOR Y IN Tab_Terminal_Info.FIRST..tab_terminal_info.LAST
    LOOP
    dbms_output.put_line(v_flag);
    v_flag := v_flag + 1;
    end loop;
    END LOOP;
    v_Errorflag := 13;
    COMMIT;
    I could not find the exact reason of error.
    Please help us to solve this error.
    Thanks and Regards..
    Dipali..

    Hello Friends,
    I got the solution.. :)
    I did one mistake in procedure where the loop should end.
    I used the statemetn: EXIT WHEN cur_terminal_info%rowcount &lt;= 0;
    But it should be: EXIT WHEN Tab_Terminal_Info.COUNT &lt;= 0;
    Now my script is working fine.. :)
    Thanks and Regards,
    Dipali..

  • Bulk collect insert using forall

    Hi all,
    in the following statement:
    declare
    cursor C is select id,PEOPLE_ID from CN_ITEMS;
    type T_A is table of cn_items%rowtype;
    V_A T_A;
    begin
    open c;
    LOOP
    fetch c bulk collect into v_a;
    forall  I in V_A.first..V_A.last 
        insert into CN_TAXES(id,CREATION_DATE,TAX_PRICE,ITEM_ID,PEOPLE_ID)
                     values (CN_TAX_S.NEXTVAL, sysdate,10.5,v_a.id(i),v_a.people_id(i));
      exit when c%notfound;
    end loop;
    end;
    /i receive error:
    ORA-06550: line 13, column 2:
    PLS-00394: wrong number of values in the INTO list of a FETCH statement
    ORA-06550: line 13, column 2:
    PL/SQL: SQL Statement ignored
    ORA-06550: line 20, column 61:
    PLS-00302: component 'ID' must be declared
    ORA-06550: line 20, column 71:
    PLS-00302: component 'PEOPLE_ID' must be declared
    ORA-06550: line 20, column 71:
    PLS-00302: component 'PEOPLE_ID' must be declared
    ORA-06550: line 20, column 67:
    PL/SQL: ORA-00904: "V_A"."PEOPLE_ID": invalid identifier
    ORA-06550: line 19, column 5:
    PL/SQL: SQL Statement ignored
    06550. 00000 -  "line %s, column %s:\n%s"
    *Cause:    Usually a PL/SQL compilation error.
    *Action:Any ideas how to use in this situation FORALL? If i select all values from one table and then i use FORALL to insert them in another table with same columns is ok, but here i just want to use values from fetching to insert them in 2 columns of other table...
    Version : 11g
    Thanks in advance,
    Bahchevanov.

    >
    Any ideas how to use in this situation FORALL? If i select all values from one table and then i use FORALL to insert them in another table with same columns is ok
    >
    You have answered your own question. The solution is exactly what you just said above
    >
    select all values from one table and then i use FORALL to insert them in another table with same columns
    >
    The first error you were getting
    PLS-00394: wrong number of values in the INTO list of a FETCH statementis because of this code
    cursor C is select id,PEOPLE_ID from CN_ITEMS;
    type T_A is table of cn_items%rowtype;Your T_A variable is based on the CN_ITEMS table but since you are using a cursor you should base it on the cursor
    cursor C is select id,PEOPLE_ID from CN_ITEMS;
    type T_A is table of C%rowtype;You are also selecting ID but never using it. And you have an OUTER loop but did not use a LIMIT clause. A straight BULK COLLECT will collect everything at once so there is no purpose for the outer loop. But you should always use a limit clause rather than any implicit one.
    So if you must use a bulk collect solution (even though your specifics should be using pure SQL) then to fix your problem change that code to this
    cursor C is select CN_TAX_S.NEXTVAL, sysdate, 10.5,PEOPLE_ID from CN_ITEMS;
    type T_A is table of C%rowtype;In other words just construct a row that will match your target table. Then you can use the FORALL to insert the entire row at once (note the LIMIT clause)
    LOOP
    fetch c bulk collect into v_a LIMIT 1000;
    forall  I in V_A.first..V_A.last 
        insert into CN_TAXES(id,CREATION_DATE,TAX_PRICE,ITEM_ID,PEOPLE_ID)
                     values (V_A(I));
      exit when c%notfound;
    end loop;

Maybe you are looking for