Generic bulk collect function

I've ran into a scenario in Forms (10g) where some of the embedded plsql could benefit from the use of Bulk Collects. Now, as I've just found out, Forms doesn't like Bulks in it's embedded code, returns a 'feature not supported in client...' error.
I attempted to write a function in the db (10g), to be called from the Form, accept a ref cursor parameter, Fetch the cursor contents using bulk into an array, then return the filled array to the Form. (It may not be performant either, but I'll never know because I didn't get it working.)
However, fatally, I can't create a record array in the function without knowing the structure of the cursor. I've googled, and not found any way to do it.
So, before I give up on it and go for a different solution, I just thought I'd check here to see if anyone else has attempted this? Or am I right in now thinking it's just not do-able.
thanks in advance.

Not a good idea to bulk collect in PL/SQL on the behalf of an external client like Forms.
A bulk collect does a single thing only - it reduces the context switching between the PL/SQL and SQL engines. If you fetch a 1000 rows one at a time, then that fetch statement will cause a 1000 context switches as PL/SQL needs to step into the SQL engine in order to copy that row's data into PL/SQL.
If you do a bulk fetch of a 100 rows/fetch, then you reduce the number of context switches to 10. A significant reduction.
Okay, so now you hook Forms (or Java/Delphi/C#/etc) into this chain and push that bulk collection from a PL/SQL variable to this client. What do you achieve? Not much as you're now offsetting that reduction in context switches with more memory that needs to be used (that PL/SQL collection buffer needs PGA memory), and you add the overheads of first pulling the data from the db buffer cache into PL/SQL and then to the client.
PL/SQL makes a very poor data cache for a client - the db buffer cache was designed exactly for that purpose. And is far superior than anything we can code in PL/SQL for the same role.
It is much simpler, more robust, to rather fetch the data directly from the db buffer cache - no intermediate PL/SQL caching and buffering when fetching data. This will also scale better and perform better.
The ideal is to use PL/SQL to implement a business logic layer, security, pre-processing, validation and other good stuff for the client - and then return a ref cursor to the client. Allowing the client to use that prepared cursor to fetch data directly from the db buffer cache.

Similar Messages

  • (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;

  • BULK COLLECT in select query inside a function

    Hi All,
    My query is :
    SELECT col1,col2,col3 FROM table_a; --( consider this is a long running query with lot of joins)
    Need to know how can i get the output of the above query from a function using BULK COLLECT.
    and i tried this:
    CREATE OR REPLACE TYPE tab_a_row
    AS OBJECT (
    col1 number(20),
    col2 number(20),
    col2 number(20)) ;
    create or replace type tab_a_nt as table of tab_a_row;
    create or replace function get_table_a
    return sys_refcursor
    is
    tab_a_recs tab_a_nt;
    rv sys_refcursor;
    begin
    SELECT tab_a_row(col1,col2,col3) BULK COLLECT INTO tab_a_recs FROM table_a;
    open rv for select * from table(tab_a_recs);
    return rv;
    end;
    Function created successfully. and i exec this from sql plus using
    SQL> var rc refcursor;
    SQL> exec :rc := get_table_a;
    BEGIN :rc := get_table_a; END;
    ERROR at line 1:
    ORA-22905: cannot access rows from a non-nested table item
    ORA-06512: at "GET_TABLE_A", line 12
    ORA-06512: at line 1
    Kindly share your ideas on how to use bulk collect and get set of outputs from a function.
    Edited by: 887268 on Apr 18, 2013 3:10 AM

    >
    If i use refcursor , then the JAVA code needs to be changed accordinglyto get the refcursor output.
    >
    Well, of course. Java has to know what the sql projection is. How else will it know how many columns there are and their datatypes.
    But that is true no matter what method you use.
    >
    But if i use a PLSQL COLLECTION TYPE (nested tables ) , then i can get output as ,
    select * from table(function_name(input1,input2));
    >
    No - using the 'table' function mean you are calling a PIPELINED function.
    This is a sample of a PIPELINED procedure.
    -- type to match emp record
    create or replace type emp_scalar_type as object
      (EMPNO NUMBER(4) ,
       ENAME VARCHAR2(10),
       JOB VARCHAR2(9),
       MGR NUMBER(4),
       HIREDATE DATE,
       SAL NUMBER(7, 2),
       COMM NUMBER(7, 2),
       DEPTNO NUMBER(2)
    -- table of emp records
    create or replace type emp_table_type as table of emp_scalar_type
    -- pipelined function
    create or replace function get_emp( p_deptno in number )
      return emp_table_type
      PIPELINED
      as
       TYPE EmpCurTyp IS REF CURSOR RETURN emp%ROWTYPE;
        emp_cv EmpCurTyp;
        l_rec  emp%rowtype;
      begin
        open emp_cv for select * from emp where deptno = p_deptno;
        loop
          fetch emp_cv into l_rec;
          exit when (emp_cv%notfound);
          pipe row( emp_scalar_type( l_rec.empno, LOWER(l_rec.ename),
              l_rec.job, l_rec.mgr, l_rec.hiredate, l_rec.sal, l_rec.comm, l_rec.deptno ) );
        end loop;
        return;
      end;
    select * from table(get_emp(20))Or your function could return a collection like the example from this thread this morning.
    Example of Collection as datatype of a function’s return value
    CREATE OR REPLACE TYPE enamelist as VARRAY(20) of VARCHAR2(20)
    /* Formatted on 4/18/2013 4:06:47 PM (QP5 v5.126.903.23003) */
    CREATE OR REPLACE FUNCTION ename_fn
    RETURN enamelist
    AS
    v_cursor_main enamelist := enamelist ();
    BEGIN
    SELECT ename
    BULK COLLECT
    INTO v_cursor_main
    FROM emp;
    RETURN v_cursor_main;
    EXCEPTION
    WHEN OTHERS
    THEN
    RETURN v_cursor_main;
    END;
    select * from table(ename_fn()) from dual;
    COLUMN_VALUE
    SMITH
    ALLEN
    WARD
    JONES
    MARTIN
    BLAKE
    CLARK
    SCOTT
    KING
    TURNER
    ADAMS
    JAMES
    FORD
    MILLER

  • Issue with Anlytical Functions,Ref Cusor and Bulk Collect

    Hi All
    pls go through the following code
    declare
    type salt is table of emp.sal%type index by binary_integer;
    st salt;
    type refc is ref cursor;
    rc refc;
    begin
    open rc for 'select max(sal) over (Partition by deptno) as Sal from emp';
    fetch rc bulk collect into st;
    close rc;
    for i in st.first..st.last loop
    dbms_output.put_LINE(st(i));
    end loop;
    end;
    When execute above code following error come :
    declare
    ERROR at line 1:
    ORA-01001: invalid cursor
    ORA-06512: at line 8
    since Anlytical functions are not supported at pl/sql,i used the ref cursor,but these record are not allowed to collect in pl/sql table.
    pls can one send a work around.
    to insert recs into pl/sql table from anlytical function.
    Thanks for Reading the Request
    Raj Ganga
    mail : [email protected]

    Just ran it exactly as it is. It works.
    I am on 9i which version are you using..
    SQL> declare
    2 type salt is table of emp.sal%type index by binary_integer;
    3 st salt;
    4 type refc is ref cursor;
    5 rc refc;
    6 begin
    7 open rc for 'select max(sal) over (Partition by deptno) as Sal from emp';
    8 fetch rc bulk collect into st;
    9 close rc;
    10 for i in st.first..st.last loop
    11 dbms_output.put_LINE(st(i));
    12 end loop;
    13 end;
    14 /
    PL/SQL procedure successfully completed.
    SQL> set serveroutput on
    SQL> /
    5000
    5000
    5000
    3000
    3000
    3000
    3000
    3000
    2850
    2850
    2850
    2850
    2850
    2850
    PL/SQL procedure successfully completed.

  • Opening two cursors using open cursor with bulk collect on colections ..

    Is it possible to have the implementatiion of using bulk collect with collections using two open cursors ..
    first c1
    second c2
    open c1
    loop
    open c2
    loop
    end loop
    close c2
    end loop;
    close c1
    what i found is for every outer loop of cursor c1 , cursor c2 is open and closed for every record.
    is this willl imporove the performace .?
    EXAMPLE:-
    NOTE: The relatoin between finc and minc is one to many ..finc is parent and minc is child
    function chk_notnull_blank ( colname IN number ) return number is
    BEGIN
    if ( colname is NOT NULL and colname not in ( -8E14, -7E14, -6E14, -5E14, -4E14, -3E14, -2E14, -1E14, -1E9 )) then
    RETURN colname ;
    else
    RETURN 0;
    end if;
    END chk_notnull_blank;
    procedure Proc_AnnualFmlyTotIncSummary is
    CURSOR c_cur_finc IS SELECT FAMID FROM FINC ;
    CURSOR c_cur_minc IS SELECT FAMID, MEMBNO , ANFEDTX, ANGOVRTX, ANPRVPNX, ANRRDEDX, ANSLTX, SALARYX, SALARYBX, NONFARMX, NONFRMBX , FARMINCX, FRMINCBX, RRRETIRX, RRRETRBX, SOCRRX, INDRETX, JSSDEDX, SSIX, SSIBX from MINC minc WHERE FAMID IN ( SELECT FAMID FROM FINC finc WHERE minc.FAMID = finc.FAMID );
    v_tot_fsalaryx number := 0;
    v_tot_fnonfrmx number := 0;
    v_tot_ffrmincx number := 0;
    v_tot_frretirx number := 0;
    v_tot_findretx number := 0;
    v_tot_fjssdedx number := 0;
    v_tot_fssix number := 0;
    v_temp_sum_fsalaryx number := 0;
    v_temp_sum_fnonfrmx number := 0;
    v_temp_sum_ffrmincx number := 0;
    v_temp_sum_frretirx number := 0;
    v_temp_sum_findretx number := 0;
    v_temp_sum_fjssdedx number := 0;
    v_temp_sum_fssix number := 0;
    TYPE minc_rec IS RECORD (FAMID MINC.FAMID%TYPE, MEMBNO MINC.MEMBNO%TYPE , ANFEDTX MINC.ANFEDTX%TYPE, ANGOVRTX MINC.ANGOVRTX%TYPE , ANPRVPNX MINC.ANPRVPNX%TYPE , ANRRDEDX MINC.ANRRDEDX%TYPE , ANSLTX MINC.ANSLTX%TYPE, SALARYX MINC.SALARYX%TYPE , SALARYBX MINC.SALARYBX%TYPE , NONFARMX MINC.NONFARMX%TYPE , NONFRMBX MINC.NONFRMBX%TYPE, FARMINCX MINC.FARMINCX%TYPE , FRMINCBX MINC.FRMINCBX%TYPE , RRRETIRX MINC.RRRETIRX%TYPE , RRRETRBX MINC.RRRETRBX%TYPE, SOCRRX MINC.SOCRRX%TYPE , INDRETX MINC.INDRETX%TYPE , JSSDEDX MINC.JSSDEDX%TYPE , SSIX MINC.SSIX%TYPE , SSIBX MINC.SSIBX%TYPE );
    v_flag_boolean boolean := false;
    v_famid number ;
    v_stmt varchar2(3200) ;
    v_limit number := 50;
    v_temp_FAMTFEDX number := 0 ;
    v_temp_FGOVRETX number := 0 ;
    v_temp_FPRIVPENX number := 0 ;
    v_temp_FRRDEDX number := 0 ;
    v_temp_FSLTAXX number := 0 ;
    v_temp_FSALARYX number := 0 ;
    v_temp_FNONFRMX number := 0 ;
    v_temp_FFRMINCX number := 0 ;
    v_temp_FRRETIRX number := 0 ;
    v_temp_FINDRETX number := 0 ;
    v_temp_FJSSDEDX number := 0 ;
    v_temp_FSSIX number := 0 ;
    BEGIN
    OPEN c_cur_finc ;
    LOOP
         FETCH c_cur_finc BULK COLLECT INTO famid_type_tbl LIMIT v_limit;
         EXIT WHEN famid_type_tbl.COUNT = 0;
         FOR i in famid_type_tbl.FIRST..famid_type_tbl.LAST
         LOOP
         OPEN c_cur_minc ;
         LOOP
         FETCH c_cur_minc BULK COLLECT INTO minc_rec_type_tbl LIMIT v_limit;
         EXIT WHEN minc_rec_type_tbl.COUNT = 0;
              FOR j IN minc_rec_type_tbl.FIRST..minc_rec_type_tbl.LAST
              LOOP
              if ( famid_type_tbl(i) = minc_rec_type_tbl(j).FAMID ) THEN
              v_temp_FAMTFEDX := v_temp_FAMTFEDX + chk_notnull_blank(minc_rec_type_tbl(j).ANFEDTX );
              v_temp_FGOVRETX := v_temp_FGOVRETX + chk_notnull_blank(minc_rec_type_tbl(j).ANGOVRTX);
              v_temp_FPRIPENX := v_temp_FPRIPENX + chk_notnull_blank(minc_rec_type_tbl(j).ANPRVPNX);
              v_temp_FRRDEDX := v_temp_FRRDEDX + chk_notnull_blank(minc_rec_type_tbl(j).ANRRDEDX);
              v_temp_FSLTAXX := v_temp_FSLTAXX + chk_notnull_blank(minc_rec_type_tbl(j).ANSLTX );
              v_temp_FSALARYX := v_temp_FSALARYX + chk_notnull_blank(minc_rec_type_tbl(j).SALARYX ) + chk_notnull_blank(minc_rec_type_tbl(j).SALARYBX);
              v_temp_FNONFRMX := v_temp_FNONFRMX + chk_notnull_blank(minc_rec_type_tbl(j).NONFARMX) + chk_notnull_blank(minc_rec_type_tbl(j).NONFRMBX);
              v_temp_FFRMINCX := v_temp_FFRMINCX + chk_notnull_blank(minc_rec_type_tbl(j).FARMINCX) + chk_notnull_blank(minc_rec_type_tbl(j).FRMINCBX );
              v_temp_FRRETIRX := v_temp_FRRETIRX + chk_notnull_blank(minc_rec_type_tbl(j).RRRETIRX) + chk_notnull_blank(minc_rec_type_tbl(j).RRRETRBX ) + chk_notnull_blank(minc_rec_type_tbl(j).SOCRRX);
              v_temp_FINDREXT := v_temp_FINDRETX + chk_notnull_blank(minc_rec_type_tbl(j).INDRETX);
              v_temp_FJSSDEDX := v_temp_FJSSDEDX + chk_notnull_blank(minc_rec_type_tbl(j).JSSDEDX);
              v_temp_FSSIX := v_temp_FSSIX + chk_notnull_blank(minc_rec_type_tbl(j).SSIX ) + chk_notnull_blank(minc_rec_type_tbl(j).SSIBX);
              END IF;
              END LOOP;
         END LOOP ;
         CLOSE c_cur_minc;
         UPDATE FINC SET FAMTFEDX = v_temp_FAMTFEDX WHERE FAMID = famid_type_tbl(i);
         END LOOP;
    END LOOP;
    CLOSE c_cur_finc;
    END;
    EXCEPTION
    WHEN OTHERS THEN
    raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
    v_err_code := SQLCODE;
    v_err_msg := substr(SQLERRM, 1, 200);
    INSERT INTO audit_table (error_number, error_message) VALUES (v_err_code, v_err_msg);
    error_logging(p_error_code => substr(sqlerrm,1,9), p_error_message => substr(sqlerrm,12), p_package =>'PKG_FCI_APP',p_procedure => 'Proc_Annual_Deductions_FromPay ' , p_location => v_location);
    end Proc_AnnualFmlyTotIncSummary ;
    Is the proga efficient and free from compilation errors ..?
    thanks/kumar
    Edited by: kumar73 on Sep 22, 2010 12:48 PM

    function chk_notnull_blank ( colname IN number ) return number is Maybe this function should have its own forum:
    how to use case in this program
    Re: how to declare a formal parameter in a function of type record and access ?
    Re: how to define a function with table type parameter
    Re: creation of db trigger with error ..
    Re: How to write a trigger for the below scenario
    how to improve the code using advanced methods
    yours advice in improving the coding ..
    How to use bulk in multiple cursors !!
    ;-)

  • Using bulk collect and for all to solve a problem

    Hi All
    I have a following problem.
    Please forgive me if its a stupid question :-) im learning.
    1: Data in a staging table xx_staging_table
    2: two Target table t1, t2 where some columns from xx_staging_table are inserted into
    Some of the columns from the staging table data are checked for valid entries and then some columns from that row will be loaded into the two target tables.
    The two target tables use different set of columns from the staging table
    When I had a thousand records there was no problem with a direct insert but it seems we will now have half a million records.
    This has slowed down the process considerably.
    My question is
    Can I use the bulk collect and for all functionality to get specific columns from a staging table, then validate the row using those columns
    and then use a bulk insert to load the data into a specific table.?
    So code would be like
    get_staging_data cursor will have all the columns i need from the staging table
    cursor get_staging_data
    is select * from xx_staging_table (about 500000) records
    Use bulk collect to load about 10000 or so records into a plsql table
    and then do a bulk insert like this
    CREATE TABLE t1 AS SELECT * FROM all_objects WHERE 1 = 2;
    CREATE OR REPLACE PROCEDURE test_proc (p_array_size IN PLS_INTEGER DEFAULT 100)
    IS
    TYPE ARRAY IS TABLE OF all_objects%ROWTYPE;
    l_data ARRAY;
    CURSOR c IS SELECT * FROM all_objects;
    BEGIN
    OPEN c;
    LOOP
    FETCH c BULK COLLECT INTO l_data LIMIT p_array_size;
    FORALL i IN 1..l_data.COUNT
    INSERT INTO t1 VALUES l_data(i);
    EXIT WHEN c%NOTFOUND;
    END LOOP;
    CLOSE c;
    END test_proc;
    In the above example t1 and the cursor have the same number of columns
    In my case the columns in the cursor loop are a small subset of the columns of table t1
    so can i use a forall to load that subset into the table t1? How does that work?
    Thanks
    J

    user7348303 wrote:
    checking if the value is valid and theres also some conditional processing rules ( such as if the value is a certain value no inserts are needed)
    which are a little more complex than I can put in a simpleWell, if the processing is too complex (and conditional) to be done in SQL, then doing that in PL/SQL is justified... but will be slower as you are now introducing an additional layer. Data now needs to travel between the SQL layer and PL/SQL layer. This is slower.
    PL/SQL is inherently serialised - and this also effects performance and scalability. PL/SQL cannot be parallelised by Oracle in an automated fashion. SQL processes can.
    To put in in simple terms. You create PL/SQL procedure Foo that processes SQL cursor and you execute that proc. Oracle cannot run multiple parallel copies of Foo. It perhaps can parallelise that SQL cursor that Foo uses - but not Foo itself.
    However, if Foo is called by the SQL engine it can run in parallel - as the SQL process calling Foo is running in parallel. So if you make Foo a pipeline table function (written in PL/SQL), and you design and code it as a thread-safe/parallel enabled function, it can be callled and used and executed in parallel, by the SQL engine.
    So moving your PL/SQL code into a parallel enabled pipeline function written in PL/SQL, and using that function via parallel SQL, can increase performance over running that same basic PL/SQL processing as a serialised process.
    This is of course assuming that the processing that needs to be done using PL/SQL code, can be designed and coded for parallel processing in this fashion.

  • Bulk collect limit 1000 is looping only 1000 records out of 35000 records

    In below code I have to loop around 35000 records for every month of the year starting from Aug-2010 to Aug-2011.
    I am using bulk collect with limit clause but the problem is:
    a: Limit clause is returning only 1000 records.
    b: It is taking too much time to process.
    CREATE OR REPLACE PACKAGE BODY UDBFINV AS
    F UTL_FILE.FILE_TYPE;
    PV_SEQ_NO NUMBER(7);
    PV_REC_CNT NUMBER(7) := 0;
    PV_CRLF VARCHAR2(2) := CHR(13) || CHR(10);
    TYPE REC_PART IS RECORD(
    PART_NUM PM_PART_HARSH.PART_NUM%TYPE,
    ON_HAND_QTY PM_PART_HARSH.ON_HAND_QTY%TYPE,
    ENGG_PREFIX PM_PART_HARSH.ENGG_PREFIX%TYPE,
    ENGG_BASE PM_PART_HARSH.ENGG_BASE%TYPE,
    ENGG_SUFFIX PM_PART_HARSH.ENGG_SUFFIX%TYPE);
    TYPE TB_PART IS TABLE OF REC_PART;
    TYPE REC_DATE IS RECORD(
    START_DATE DATE,
    END_DATE DATE);
    TYPE TB_MONTH IS TABLE OF REC_DATE;
    PROCEDURE MAIN IS
    /* To be called in Scheduler Programs Action */
    BEGIN
    /* Initializing package global variables;*/
    IFMAINT.V_PROG_NAME := 'FULL_INVENTORY';
    IFMAINT.V_ERR_LOG_TAB := 'UDB_ERR_FINV';
    IFMAINT.V_HIST_TAB := 'UDB_HT_FINV';
    IFMAINT.V_UTL_DIR_NAME := 'UDB_SEND';
    IFMAINT.V_PROG_TYPE := 'S';
    IFMAINT.V_IF_TYPE := 'U';
    IFMAINT.V_REC_CNT := 0;
    IFMAINT.V_DEL_INS := 'Y';
    IFMAINT.V_KEY_INFO := NULL;
    IFMAINT.V_MSG := NULL;
    IFMAINT.V_ORA_MSG := NULL;
    IFSMAINT.V_FILE_NUM := IFSMAINT.V_FILE_NUM + 1;
    IFMAINT.LOG_ERROR; /*Initialize error log table, delete prev. rows*/
    /*End of initialization section*/
    IFMAINT.SET_INITIAL_PARAM;
    IFMAINT.SET_PROGRAM_PARAM;
    IFMAINT.SET_UTL_DIR_PATH;
    IFMAINT.GET_DEALER_PARAMETERS;
    PV_SEQ_NO := IFSMAINT.GENERATE_FILE_NAME;
    IF NOT CHECK_FILE_EXISTS THEN
    WRITE_FILE;
    END IF;
    IF IFMAINT.V_BACKUP_PATH_SEND IS NOT NULL THEN
    IFMAINT.COPY_FILE(IFMAINT.V_UTL_DIR_PATH,
    IFMAINT.V_FILE_NAME,
    IFMAINT.V_BACKUP_PATH_SEND);
    END IF;
    IFMAINT.MOVE_FILE(IFMAINT.V_UTL_DIR_PATH,
    IFMAINT.V_FILE_NAME,
    IFMAINT.V_FILE_DEST_PATH);
    COMMIT;
    EXCEPTION
    WHEN IFMAINT.E_TERMINATE THEN
    IFMAINT.V_DEL_INS := 'N';
    IFMAINT.LOG_ERROR;
    ROLLBACK;
    UTL_FILE.FCLOSE(F);
    IFMAINT.DELETE_FILE(IFMAINT.V_UTL_DIR_PATH, IFMAINT.V_FILE_NAME);
    RAISE_APPLICATION_ERROR(IFMAINT.V_USER_ERRCODE, IFMAINT.V_ORA_MSG);
    WHEN OTHERS THEN
    IFMAINT.V_DEL_INS := 'N';
    IFMAINT.V_MSG := 'ERROR IN MAIN PROCEDURE ||IFMAINT.V_PROG_NAME';
    IFMAINT.V_ORA_MSG := SUBSTR(SQLERRM, 1, 255);
    IFMAINT.V_USER_ERRCODE := -20101;
    IFMAINT.LOG_ERROR;
    ROLLBACK;
    UTL_FILE.FCLOSE(F);
    IFMAINT.DELETE_FILE(IFMAINT.V_UTL_DIR_PATH, IFMAINT.V_FILE_NAME);
    RAISE_APPLICATION_ERROR(IFMAINT.V_USER_ERRCODE, IFMAINT.V_ORA_MSG);
    END;
    PROCEDURE WRITE_FILE IS
    CURSOR CR_PART IS
    SELECT A.PART_NUM, ON_HAND_QTY, ENGG_PREFIX, ENGG_BASE, ENGG_SUFFIX
    FROM PM_PART_HARSH A;
    lv_cursor TB_PART;
    LV_CURR_MONTH NUMBER;
    LV_MONTH_1 NUMBER := NULL;
    LV_MONTH_2 NUMBER := NULL;
    LV_MONTH_3 NUMBER := NULL;
    LV_MONTH_4 NUMBER := NULL;
    LV_MONTH_5 NUMBER := NULL;
    LV_MONTH_6 NUMBER := NULL;
    LV_MONTH_7 NUMBER := NULL;
    LV_MONTH_8 NUMBER := NULL;
    LV_MONTH_9 NUMBER := NULL;
    LV_MONTH_10 NUMBER := NULL;
    LV_MONTH_11 NUMBER := NULL;
    LV_MONTH_12 NUMBER := NULL;
    lv_month TB_MONTH := TB_MONTH();
    BEGIN
    IF CR_PART%ISOPEN THEN
    CLOSE CR_PART;
    END IF;
    FOR K IN 1 .. 12 LOOP
    lv_month.EXTEND();
    lv_month(k).start_date := ADD_MONTHS(TRUNC(SYSDATE, 'MM'), - (K + 1));
    lv_month(k).end_date := (ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -K) - 1);
    END LOOP;
    F := utl_file.fopen(IFMAINT.V_UTL_DIR_NAME, IFMAINT.V_FILE_NAME, 'W');
    IF UTL_FILE.IS_OPEN(F) THEN
    /*FILE HEADER*/
    utl_file.put_line(F,
    RPAD('$CUD-', 5, ' ') ||
    RPAD(SUBSTR(IFMAINT.V_PANDA_CD, 1, 5), 5, ' ') ||
    RPAD('-136-', 5, ' ') || RPAD('000000', 6, ' ') ||
    RPAD('-REDFLEX-KA-', 13, ' ') ||
    RPAD('00000000-', 9, ' ') ||
    RPAD(IFMAINT.V_CDS_SPEC_REL_NUM, 5, ' ') ||
    RPAD('CD', 2, ' ') ||
    RPAD(TO_CHAR(SYSDATE, 'MMDDYY'), 6, ' ') ||
    LPAD(IFSMAINT.V_FILE_NUM, 2, 0) ||
    RPAD('-', 1, ' ') || RPAD(' ', 9, ' ') ||
    RPAD('-', 1, ' ') || RPAD(' ', 17, ' ') ||
    RPAD('CD230', 5, ' ') ||
    RPAD(TO_CHAR(SYSDATE, 'MMDDYY'), 6, ' ') ||
    LPAD(IFSMAINT.V_FILE_NUM, 2, 0) ||
    LPAD(PV_REC_CNT, 8, 0) || RPAD(' ', 5, ' ') ||
    RPAD('00000000', 8, ' ') || RPAD('CUD', 3, ' ') ||
    RPAD(IFMAINT.V_CDS_SPEC_REL_NUM, 5, ' ') ||
    RPAD(IFMAINT.V_GEO_SALES_AREA_CD, 3, ' ') ||
    RPAD(IFMAINT.V_FRANCHISE_CD, 2, ' ') ||
    RPAD(IFMAINT.V_DSP_REL_NUM, 9, ' ') ||
    RPAD('00136REDFLEX', 12, ' ') || RPAD(' ', 1, ' ') ||
    RPAD('KA', 2, ' ') || RPAD('000000', 6, ' ') ||
    RPAD('00D', 3, ' ') ||
    RPAD(IFMAINT.V_VENDOR_ID, 6, ' ') ||
    RPAD(IFSMAINT.V_FILE_TYPE, 1, ' ') ||
    RPAD('>', 1, ' ') || PV_CRLF);
    /*LINE ITEMS*/
    OPEN CR_PART;
    FETCH CR_PART BULK COLLECT
    INTO lv_cursor limit 1000;
    FOR I IN lv_cursor.FIRST .. lv_cursor.LAST LOOP
    SELECT SUM(A.BILL_QTY)
    INTO LV_CURR_MONTH
    FROM PD_ISSUE A, PH_ISSUE B
    WHERE A.DOC_TYPE IN ('CRI', 'RRI', 'RSI', 'CSI')
    AND A.DOC_NUM = B.DOC_NUM
    AND B.DOC_DATE BETWEEN TRUNC(SYSDATE, 'MM') AND SYSDATE
    AND A.PART_NUM = LV_CURSOR(i).PART_NUM;
    FOR J IN 1 .. 12 LOOP
    SELECT SUM(A.BILL_QTY)
    INTO LV_MONTH_1
    FROM PD_ISSUE A, PH_ISSUE B
    WHERE A.DOC_TYPE IN ('CRI', 'RRI', 'RSI', 'CSI')
    AND A.DOC_NUM = B.DOC_NUM
    AND B.DOC_DATE BETWEEN lv_month(J).start_date and lv_month(J)
    .end_date
    AND A.PART_NUM = LV_CURSOR(i).PART_NUM;
    END LOOP;
    utl_file.put_line(F,
    RPAD('IL', 2, ' ') ||
    RPAD(TO_CHAR(SYSDATE, 'RRRRMMDD'), 8, ' ') ||
    RPAD(LV_CURSOR(I).ENGG_PREFIX, 6, ' ') ||
    RPAD(LV_CURSOR(I).ENGG_BASE, 8, ' ') ||
    RPAD(LV_CURSOR(I).ENGG_SUFFIX, 6, ' ') ||
    LPAD(LV_CURSOR(I).ON_HAND_QTY, 7, 0) ||
    LPAD(NVL(LV_CURR_MONTH, 0), 7, 0) ||
    LPAD(LV_MONTH_1, 7, 0) || LPAD(LV_MONTH_2, 7, 0) ||
    LPAD(LV_MONTH_3, 7, 0) || LPAD(LV_MONTH_4, 7, 0) ||
    LPAD(LV_MONTH_5, 7, 0) || LPAD(LV_MONTH_6, 7, 0) ||
    LPAD(LV_MONTH_7, 7, 0) || LPAD(LV_MONTH_8, 7, 0) ||
    LPAD(LV_MONTH_9, 7, 0) || LPAD(LV_MONTH_10, 7, 0) ||
    LPAD(LV_MONTH_11, 7, 0) ||
    LPAD(LV_MONTH_12, 7, 0));
    IFMAINT.V_REC_CNT := IFMAINT.V_REC_CNT + 1;
    END LOOP;
    CLOSE CR_PART;
    /*TRAILER*/
    utl_file.put_line(F,
    RPAD('$EOF-', 5, ' ') || RPAD('320R', 4, ' ') ||
    RPAD(SUBSTR(IFMAINT.V_PANDA_CD, 1, 5), 5, ' ') ||
    RPAD(' ', 5, ' ') ||
    RPAD(IFMAINT.V_GEO_SALES_AREA_CD, 3, ' ') ||
    RPAD(TO_CHAR(SYSDATE, 'MM-DD-RR'), 6, ' ') ||
    LPAD(IFSMAINT.V_FILE_NUM, 2, 0) ||
    LPAD(IFMAINT.V_REC_CNT, 8, 0) || 'H' || '>' ||
    IFMAINT.V_REC_CNT);
    utl_file.fclose(F);
    IFMAINT.INSERT_HISTORY;
    END IF;
    END;
    FUNCTION CHECK_FILE_EXISTS RETURN BOOLEAN IS
    LB_FILE_EXIST BOOLEAN := FALSE;
    LN_FILE_LENGTH NUMBER;
    LN_BLOCK_SIZE NUMBER;
    BEGIN
    UTL_FILE.FGETATTR(IFMAINT.V_UTL_DIR_NAME,
    IFMAINT.V_FILE_NAME,
    LB_FILE_EXIST,
    LN_FILE_LENGTH,
    LN_BLOCK_SIZE);
    IF LB_FILE_EXIST THEN
    RETURN TRUE;
    END IF;
    RETURN FALSE;
    EXCEPTION
    WHEN OTHERS THEN
    RETURN FALSE;
    END;
    END;

    Try this:
    OPEN CR_PART;
    loop
    FETCH CR_PART BULK COLLECT
    INTO lv_cursor limit 1000;
    exit when CR_PART%notfound;
    FOR I IN lv_cursor.FIRST .. lv_cursor.LAST LOOP
    SELECT SUM(A.BILL_QTY)
    INTO LV_CURR_MONTH
    FROM PD_ISSUE A, PH_ISSUE B
    WHERE A.DOC_TYPE IN ('CRI', 'RRI', 'RSI', 'CSI')
    AND A.DOC_NUM = B.DOC_NUM
    AND B.DOC_DATE BETWEEN TRUNC(SYSDATE, 'MM') AND SYSDATE
    AND A.PART_NUM = LV_CURSOR(i).PART_NUM;
    FOR J IN 1 .. 12 LOOP
    SELECT SUM(A.BILL_QTY)
    INTO LV_MONTH_1
    FROM PD_ISSUE A, PH_ISSUE B
    WHERE A.DOC_TYPE IN ('CRI', 'RRI', 'RSI', 'CSI')
    AND A.DOC_NUM = B.DOC_NUM
    AND B.DOC_DATE BETWEEN lv_month(J).start_date and lv_month(J)
    .end_date
    AND A.PART_NUM = LV_CURSOR(i).PART_NUM;
    END LOOP;
    utl_file.put_line(F,
    RPAD('IL', 2, ' ') ||
    RPAD(TO_CHAR(SYSDATE, 'RRRRMMDD'), 8, ' ') ||
    RPAD(LV_CURSOR(I).ENGG_PREFIX, 6, ' ') ||
    RPAD(LV_CURSOR(I).ENGG_BASE, 8, ' ') ||
    RPAD(LV_CURSOR(I).ENGG_SUFFIX, 6, ' ') ||
    LPAD(LV_CURSOR(I).ON_HAND_QTY, 7, 0) ||
    LPAD(NVL(LV_CURR_MONTH, 0), 7, 0) ||
    LPAD(LV_MONTH_1, 7, 0) || LPAD(LV_MONTH_2, 7, 0) ||
    LPAD(LV_MONTH_3, 7, 0) || LPAD(LV_MONTH_4, 7, 0) ||
    LPAD(LV_MONTH_5, 7, 0) || LPAD(LV_MONTH_6, 7, 0) ||
    LPAD(LV_MONTH_7, 7, 0) || LPAD(LV_MONTH_8, 7, 0) ||
    LPAD(LV_MONTH_9, 7, 0) || LPAD(LV_MONTH_10, 7, 0) ||
    LPAD(LV_MONTH_11, 7, 0) ||
    LPAD(LV_MONTH_12, 7, 0));
    IFMAINT.V_REC_CNT := IFMAINT.V_REC_CNT + 1;
    END LOOP;
    end loop;
    CLOSE CR_PART;

  • 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

  • How to use bulk collect into clause

    hi all,
    i have requirement like this i want to change the query by passing the different table names in oracle database 10gr2.
    like if i use first i pass the scott.emp table name select * from scott.emp;
    then i want pass scott.dept table name select * from scott.dept;
    using select * option in the select list.
    how can i execute it.
    give me any solution.
    please reply....

    Hi,
    i recently also ran into some serious trouble to make dynamic sql work.
    It was about parallel pipelined function with strongly typed cursor (for "partition ... by hash(...)").
    But in addition requiring dynamic SQL for the input to this cursor.
    I couldn't make it work with execute immediate or something else.
    So i used another way - I translated dynamic SQL into dynamically created static SQL:
    1. create a base SQL data object type with abstract interface for code (e.g. some Execute() member function).
    2. dynamically create a derived SQL data object type with concrete implementation of Execute() holding your "dynamic SQL" "in a static way"
    3. delegate to Execute().
    Let me quote my example from comp.databases.oracle.server, thread "dynamically created cursor doesn't work for parallel pipelined functions"
    - pls. see below (it's an old one - with likely some odd stuff inside).
    It might sound some kind of strange for DB programmer.
    (I come from C++. Though this is not an excuse smile)
    Maybe i just missed another possible option to handle the problem.
    And it's a definitely verbose.
    But i think it's at least a (last) option.
    Actually i would be interested to hear, what others think about it.
    best regards,
    Frank
    --------------- snip -------------------------
    drop table parallel_test;
    drop type MyDoit;
    drop type BaseDoit;
    drop type ton;
    create type ton as table of number;
    CREATE TABLE parallel_test (
    id NUMBER(10),
    description VARCHAR2(50)
    BEGIN
    FOR i IN 1 .. 100000 LOOP
    INSERT INTO parallel_test (id, description)
    VALUES (i, 'Description or ' || i);
    END LOOP;
    COMMIT;
    END;
    create or replace type BaseDoit as object (
    id number,
    static function make(p_id in number)
    return BaseDoit,
    member procedure doit(
    p_sids in out nocopy ton,
    p_counts in out nocopy ton)
    ) not final;
    create or replace type body BaseDoit as
    static function make(p_id in number)
    return BaseDoit
    is
    begin
    return new BaseDoit(p_id);
    end;
    member procedure doit(
    p_sids in out nocopy ton,
    p_counts in out nocopy ton)
    is
    begin
    dbms_output.put_line('BaseDoit.doit(' || id || ') invoked...');
    end;
    end;
    -- Define a strongly typed REF CURSOR type.
    CREATE OR REPLACE PACKAGE parallel_ptf_api AS
    TYPE t_parallel_test_row IS RECORD (
    id1 NUMBER(10),
    desc1 VARCHAR2(50),
    id2 NUMBER(10),
    desc2 VARCHAR2(50),
    sid NUMBER
    TYPE t_parallel_test_tab IS TABLE OF t_parallel_test_row;
    TYPE t_parallel_test_ref_cursor IS REF CURSOR RETURN
    t_parallel_test_row;
    FUNCTION test_ptf (p_cursor IN t_parallel_test_ref_cursor)
    RETURN t_parallel_test_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY any);
    END parallel_ptf_api;
    SHOW ERRORS
    CREATE OR REPLACE PACKAGE BODY parallel_ptf_api AS
    FUNCTION test_ptf (p_cursor IN t_parallel_test_ref_cursor)
    RETURN t_parallel_test_tab PIPELINED
    PARALLEL_ENABLE(PARTITION p_cursor BY any)
    IS
    l_row t_parallel_test_row;
    BEGIN
    LOOP
    FETCH p_cursor
    INTO l_row;
    EXIT WHEN p_cursor%NOTFOUND;
    select userenv('SID') into l_row.sid from dual;
    PIPE ROW (l_row);
    END LOOP;
    RETURN;
    END test_ptf;
    END parallel_ptf_api;
    SHOW ERRORS
    PROMPT
    PROMPT Serial Execution
    PROMPT ================
    SELECT sid, count(*)
    FROM TABLE(parallel_ptf_api.test_ptf(CURSOR(SELECT t1.id,
    t1.description, t2.id, t2.description, null
    FROM parallel_test t1,
    parallel_test t2
    where t1.id = t2.id
    ) t2
    GROUP BY sid;
    PROMPT
    PROMPT Parallel Execution
    PROMPT ==================
    SELECT sid, count(*)
    FROM TABLE(parallel_ptf_api.test_ptf(CURSOR(SELECT /*+
    parallel(t1,5) */ t1.id, t1.description, t2.id, t2.description, null
    FROM parallel_test t1,
    parallel_test t2
    where t1.id = t2.id
    ) t2
    GROUP BY sid;
    PROMPT
    PROMPT Parallel Execution 2
    PROMPT ==================
    set serveroutput on;
    declare
    v_sids ton := ton();
    v_counts ton := ton();
    -- v_cur parallel_ptf_api.t_parallel_test_ref_cursor;
    v_cur sys_refcursor;
    procedure OpenCursor(p_refCursor out sys_refcursor)
    is
    begin
    open p_refCursor for 'SELECT /*+ parallel(t1,5) */ t1.id,
    t1.description, t2.id, t2.description, null
    FROM parallel_test t1,
    parallel_test t2
    where t1.id = t2.id';
    end;
    begin
    OpenCursor(v_cur);
    SELECT sid, count(*) bulk collect into v_sids, v_counts
    FROM TABLE(parallel_ptf_api.test_ptf(v_cur)) t2
    GROUP BY sid;
    for i in v_sids.FIRST.. v_sids.LAST loop
    dbms_output.put_line (v_sids(i) || ', ' || v_counts(i));
    end loop;
    end;
    PROMPT
    PROMPT Parallel Execution 3
    PROMPT ==================
    set serveroutput on;
    declare
    instance BaseDoit;
    v_sids ton := ton();
    v_counts ton := ton();
    procedure CreateMyDoit
    is
    cmd varchar2(4096 char);
    begin
    cmd := 'create or replace type MyDoit under BaseDoit ( ' ||
    ' static function make(p_id in number) ' ||
    ' return MyDoit, ' ||
    ' overriding member procedure doit( ' ||
    ' p_sids in out nocopy ton, ' ||
    ' p_counts in out nocopy ton) ' ||
    execute immediate cmd;
    cmd := 'create or replace type body MyDoit as ' ||
    ' static function make(p_id in number) ' ||
    ' return MyDoit ' ||
    ' is ' ||
    ' begin ' ||
    ' return new MyDoit(p_id); ' ||
    ' end; ' ||
    ' ' ||
    ' overriding member procedure doit( ' ||
    ' p_sids in out nocopy ton, ' ||
    ' p_counts in out nocopy ton) ' ||
    ' is ' ||
    ' begin ' ||
    ' dbms_output.put_line(''MyDoit.doit('' || id || '')
    invoked...''); ' ||
    ' SELECT sid, count(*) bulk collect into p_sids, p_counts ' ||
    ' FROM TABLE(parallel_ptf_api.test_ptf(CURSOR( ' ||
    ' SELECT /*+ parallel(t1,5) */ t1.id, t1.description,
    t2.id, t2.description, null ' ||
    ' FROM parallel_test t1, parallel_test t2 ' ||
    ' where t1.id = t2.id ' ||
    ' ))) ' ||
    ' GROUP BY sid; ' ||
    ' end; ' ||
    ' end; ';
    execute immediate cmd;
    end;
    begin
    CreateMyDoit;
    execute immediate 'select MyDoit.Make(11) from dual' into instance;
    instance.doit(v_sids, v_counts);
    if v_sids.COUNT > 0 then
    for i in v_sids.FIRST.. v_sids.LAST loop
    dbms_output.put_line (v_sids(i) || ', ' || v_counts(i));
    end loop;
    end if;
    end;
    --------------- snap -------------------------

  • 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

  • Bulk collect into using vector in where clause

    Hi,
    I have a java stored procedure which takes an array as input and returns an array as output.
    I want to select a column in to a collection of array, using array as the select criteria.
    For example
    create or replace type My_list as table of varchar(20);
    create or replace type outarray as table of BLOB;
    create or replace function myfunction ( inputlist My_list)
    return outarray
    AS
    o_data := outarray();
    BEGIN
    SELECT mycolumn from mytable BULK COLLECT INTO o_data where my_creteria=inputlist
    retrun o_data;
    END;
    Unless I use FORALL I can not retrieve the inputlist values. I am not sure how I can use the bulk collect and forall in the same statement?
    My data is arranged such that , for each value in the inputlist one row will be fetched. I want to fetch all these rows at one go using the inputlist as select criteria,
    I can use the for i in inputlist.first .. inputlist.LAST and iterate the inputlist but that process is quite expensive interms of the latency.
    You help in optimizing my query would be highly appreciated.
    Regards,
    Syam

    That only leaves ugly solutions, like:
    <ul>
    <li>Using dynamic SQL to create a temporary table, doing a bulk insert of the incoming array and then reading the table values using a subquery with the IN opeartor.</li>
    <li>Building a dyanmic list, like ('value1','value2', ..) and use it with the IN operator, this is a variable list and requires DBMS_SQL package.</li>
    <li>Ignore the bulk operations. :-(</li>
    </ul>

  • Bulk collection PL/SQL table

    Hi all,
    10g version 10.2.0.1
    What approach can I take to accomplish the following.
    I need to build a collection based on the result set of two SQL statements within a loop.
    Example:
    FUNCTION( get_info )IS
    RETURN retrieval_pkg_public_ty
    PIPELINED
    TYPE ret_tab IS TABLE OF ret_rec;
    FOR i IN 1 .. 2
    LOOP
    SELECT...
    BULK COLLECT into ret_tbl
    FROM(SELECT...
    FROM(SELECT..
    quite a large SQL statement...
    WHERE
    x = parameter_value, --parameter changes on based on i values
    y = parameter_value, --parameter changes on based on i values
    END LOOP;
    FOR i IN ret_tbl.FIRST..ret_tbl.LAST
    LOOP
    PIPE ROW...
    END LOOP;
    I can use a global temporary table to hold the results of each loop and them select from gtt, however I would prefer to use a table function.
    thanks
    Mark

    user1602736 wrote:
    Currently, I have a procedure that is called within a package that returns a SYS_REFCURSOR.
    Current code in procedure is
    FOR i IN 1..2
    LOOP
    INSERt INTO gtt
    END LOOP;Why not simply populate the GTT using an INSERT INTO gtt SELECT ... FROM source WHERE .. ?
    I wanted to avoid creating a gtt to accomplish above.Why? What problems with this GTT approach do you think there are?
    The cursor only returns around 50 records. The record has around 20 data fieldsRemember that if you store this as a PL/SQL collection (there's no such thing as PL "+table+"), this collection resides in expensive private process memory (the PGA). If there a 100 sessions using this code, then there will be a 100 copies of this collection.
    On the other hand, a GTT does not reside in expensive private memory. And it can scale to a 1000 rows in future, without affecting performance (remember that GTTs can be indexed - collections not). For a collection, you will pay an excessive price in memory for keeping that 1000 rows in the PGA.
    GTTs are not a bad thing. Collections are not a bad thing. They are tools for addressing specific problems. Your task is to select the right tool for the job. Caching SQL row data in a PL/SQL collection is almost never the right tool for the job, as it requires private process memory and uses a very simplistic data structure (does not support indexes and so on).

  • Bulk collect in pl/sql

    Hi there, need help, is it a right way of using bulk collect here?  As I could not get the correct result. 
    The problem is the 'where clause' is only taking 'SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE' column name
    to output but not for the rest of columns. The output given is only 3 records from  'SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE'. 
    In fact, other column names have records existed.  Please advise.  Regards, BE.
    FOR i in 1..l_flex_column_list.count LOOP
         dbms_output.put_line('SEAEGO_CC_FAMILYMODEL_INFO_'||l_flex_column_list(i).column_name);
            BEGIN                   
            SELECT evsvv.value_set_name, evsvv.internal_name, evsvv.display_name
                BULK COLLECT INTO l_flex_value_list  -- table collection
               FROM ego_value_set_values_v evsvv
                *WHERE evsvv.value_set_name= 'SEAEGO_CC_FAMILYMODEL_INFO_'||l_flex_column_list(i).column_name;*
         EXCEPTION
           WHEN NO_DATA_FOUND THEN
              l_flex_value_list:=NULL;
           WHEN OTHERS THEN
              dbms_output.put_line('test ');
         END;
         END LOOP;
          dbms_output.put_line('');
          BEGIN
            FOR i in 1..l_flex_value_list.count LOOP
            dbms_output.put_line(l_flex_value_list(i).value_set_name ||' '||
                           l_flex_value_list(i).internal_name||' '||
                           l_flex_value_list(i).display_name);
            END LOOP;
          END;
    END;
    *Output*
    SEAEGO_CC_FAMILYMODEL_INFO_PRODUCT_TYPE
    SEAEGO_CC_FAMILYMODEL_INFO_INTERNAL_PRODUCT_NAME
    SEAEGO_CC_FAMILYMODEL_INFO_NUMBER_OF_HEADS
    SEAEGO_CC_FAMILYMODEL_INFO_NUMBER_OF_DISCS
    SEAEGO_CC_FAMILYMODEL_INFO_GENERATION
    SEAEGO_CC_FAMILYMODEL_INFO_DESIGN_APPLICATION
    SEAEGO_CC_FAMILYMODEL_INFO_FORM_FACTOR
    SEAEGO_CC_FAMILYMODEL_INFO_INTERFACE
    SEAEGO_CC_FAMILYMODEL_INFO_BASE_WARRANTY_MONTHS
    SEAEGO_CC_FAMILYMODEL_INFO_PHYSICAL_SECTOR_SIZE
    SEAEGO_CC_FAMILYMODEL_INFO_MODEL_HEIGHT
    SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE
    _incorrect result_
    SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE NO ENCRYPTION No Encryption
    SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE FDE BASE FDE Base
    SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE FDE FIPS 140-2 FDE FIPS 140-2
    Query Statement for other columns
    SELECT evsvv.value_set_name, evsvv.internal_name, evsvv.display_name
                FROM ego_value_set_values_v evsvv
            WHERE evsvv.value_set_name  = 'SEAEGO_CC_FAMILYMODEL_INFO_MODEL_HEIGHT'
    *Output*
    SEAEGO_CC_FAMILYMODEL_INFO_MODEL_HEIGHT     5MM     0.196 IN/5MM
    SEAEGO_CC_FAMILYMODEL_INFO_MODEL_HEIGHT     7MM     0.276 IN/7MM
    SEAEGO_CC_FAMILYMODEL_INFO_MODEL_HEIGHT     8MM     0.315 IN/8MM
    SEAEGO_CC_FAMILYMODEL_INFO_MODEL_HEIGHT     9.5MM     0.374 IN/9.5MM
    SEAEGO_CC_FAMILYMODEL_INFO_MODEL_HEIGHT     10.5MM     0.431 IN/10.5MM

    Hi there, the select statement below actually query record into 'l_flex_value_list' record variable by
    looping in different values e.g. SEAEGO_CC_FAMILYMODEL_INFO_BASE_WARRANTY_MONTHS,
    SEAEGO_CC_FAMILYMODEL_INFO_PHYSICAL_SECTOR_SIZE and SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE.
    But, my problem is the 'l_flex_value_list' is only storing the result for SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE, overwriting
    the values of SEAEGO_CC_FAMILYMODEL_INFO_BASE_WARRANTY_MONTHS and SEAEGO_CC_FAMILYMODEL_INFO_PHYSICAL_SECTOR_SIZE.
    What is the logic should be in this case?
       FOR i in 1..l_flex_column_list.count LOOP
      --   dbms_output.put_line('SEAEGO_CC_FAMILYMODEL_INFO_'||l_flex_column_list(i).column_name);
        dbms_output.put_line(l_flex_column_list(i).column_name||' '||
                          l_flex_column_list(i).column_value);
            BEGIN                   
            SELECT evsvv.value_set_name, evsvv.internal_name, evsvv.display_name
            BULK COLLECT INTO l_flex_value_list
            FROM ego_value_set_values_v evsvv
            WHERE evsvv.value_set_name= 'SEAEGO_CC_FAMILYMODEL_INFO_'||l_flex_column_list(i).column_name;
          --  AND internal_name = l_flex_column_list(i).column_value;
         EXCEPTION
           WHEN NO_DATA_FOUND THEN
              l_flex_value_list:=NULL;
           WHEN OTHERS THEN
              dbms_output.put_line(' ');
         END;
         END LOOP;
    ------------------the PL/SQL -----------------------
    Declare
    l_query  VARCHAR2(2000);
    l_where VARCHAR2(2000);
    l_bind_var1      VARCHAR2(40);
    l_bind_var2      VARCHAR2(40);
    l_sql            NUMBER:=0;
    l_column_name    VARCHAR2(100);
    l_column_value   VARCHAR2(200);
    p_item_id NUMBER :=NULL;--931104 ;
    p_item_number       VARCHAR2(40):='9NH2D4-570';
    p_stmodel  VARCHAR2(40):='ST901603FGD1E1-RK';
    TYPE l_flex_column_rec IS RECORD
        column_name   varchar2(100),
        column_value  varchar2(100));
    TYPE l_flex_column_tab IS TABLE OF l_flex_column_rec;
    l_flex_column_list l_flex_column_tab;
    TYPE l_flex_value_rec IS RECORD
    ( value_set_name   fnd_flex_value_sets.flex_value_set_name%type,
       internal_name    fnd_flex_values.flex_value%type,
       display_name     fnd_flex_values_tl.description%type);
    TYPE l_flex_value_tab IS TABLE OF l_flex_value_rec;
    l_flex_value_list l_flex_value_tab;
    BEGIN
       IF p_stmodel is not null THEN
          IF p_item_number IS NULL AND p_item_id IS NULL THEN
           l_where  :='and st.stmodelnumber= :1 and sp.detailedproductname=''GENERIC''';
           l_bind_var1 := p_stmodel;
           l_sql := 1;
          ELSIF p_item_number IS NULL AND p_item_id IS NOT NULL THEN
           l_where  :='and st.stmodelnumber=:1 and sc.ccitemnumber = (select msi.segment1                       
                       from mtl_system_items msi                       
                       where msi.inventory_item_id = :2 and rownum=1)';
           l_bind_var1 := p_stmodel;
           l_bind_var2 := p_item_id;
           l_sql :=2;
          ELSE
           l_where  :='and st.stmodelnumber=:1 and sc.ccitemnumber = (select msi.segment1                      
                       from mtl_system_items msi                      
                       where msi.segment1=:2 and rownum=1)';
           l_bind_var1 := p_stmodel;
           l_bind_var2 := p_item_number;
           l_sql :=2;
          END IF; 
       ELSE
         IF p_item_id is null and p_item_number is not null THEN
                l_where := 'and sc.ccitemnumber = (select msi.segment1                      
                           from mtl_system_items msi                      
                           where msi.segment1=:1 and rownum=1)';
                l_bind_var1 :=  p_item_number;
                l_sql := 1;
            ELSIF p_item_id is NOT null and p_item_number is null THEN
                l_where := 'and sc.ccitemnumber = ( select msi.segment1                       
                            from mtl_system_items msi                       
                            where msi.inventory_item_id = :1 and rownum=1)';
                 l_bind_var1 := p_item_id;
                 l_sql := 1;
            ELSIF p_item_id is not null and p_item_number is not null THEN
                l_where :='and sc.ccitemnumber = (select msi.segment1                      
                           from mtl_system_items msi                      
                           where msi.inventory_item_id =:1                      
                           AND msi.segment1=:2 and rownum=1)';
                l_bind_var1 := p_item_id;
                l_bind_var2 := p_item_number;
                l_sql :=2;
            END IF;
        END IF;
    l_query :='select decode(rn,1,''PRODUCT_TYPE'',2,''INTERNAL_PRODUCT_NAME'',3,''NUMBER_OF_HEADS'',4,''NUMBER_OF_DISCS'',5,''GENERATION'',6,''DESIGN_APPLICATION'',
            7,''FORM_FACTOR'',8,''INTERFACE'',9,''BASE_WARRANTY_MONTHS'',10,''PHYSICAL_SECTOR_SIZE'',11,''MODEL_HEIGHT'',''ENCRYPTION_TYPE'') as column_name,
            decode(rn,1,st.producttype,2,sp.internalproductname,3,sp.numberofheads,4,sp.numberofdiscs,5,sp.generation,6,sp.designapplication,7,st.formfactor,
            8,st.interface,9,st.warrantymonths,10,st.physical_sector_size,11,st.modelheight,st.encryption_type) as column_value
            FROM  seaeng_productmodel sp , seaeng_stmodel st, seaeng_ccitemnumber sc , (select rownum rn from dual connect by rownum <=12)
            WHERE sc.productmodelnumber = sp.productmodelnumber
            AND sp.stmodelnumber = st.stmodelnumber ';
      IF l_sql = 1 THEN
          EXECUTE IMMEDIATE  l_query ||l_where
                             BULK COLLECT INTO l_flex_column_list
                             USING l_bind_var1 ;
                     --        dbms_output.put_line(l_query||l_where);
        ELSIF
             l_sql =2 THEN
            EXECUTE IMMEDIATE l_query || l_where
                             BULK COLLECT INTO l_flex_column_list
                             using l_bind_var1,l_bind_var2;
                  dbms_output.put_line(l_query||l_where);
        END IF;
       FOR i in 1..l_flex_column_list.count LOOP
      --   dbms_output.put_line('SEAEGO_CC_FAMILYMODEL_INFO_'||l_flex_column_list(i).column_name);
        dbms_output.put_line(l_flex_column_list(i).column_name||' '||
                          l_flex_column_list(i).column_value);
            BEGIN                   
            SELECT evsvv.value_set_name, evsvv.internal_name, evsvv.display_name
            BULK COLLECT INTO l_flex_value_list
            FROM ego_value_set_values_v evsvv
            WHERE evsvv.value_set_name= 'SEAEGO_CC_FAMILYMODEL_INFO_'||l_flex_column_list(i).column_name;
          --  AND internal_name = l_flex_column_list(i).column_value;
         EXCEPTION
           WHEN NO_DATA_FOUND THEN
              l_flex_value_list:=NULL;
           WHEN OTHERS THEN
              dbms_output.put_line(' ');
         END;
         END LOOP;
          dbms_output.put_line('');
          BEGIN
            FOR i in 1..l_flex_value_list.count LOOP
            dbms_output.put_line(l_flex_value_list(i).value_set_name ||' '||
                           l_flex_value_list(i).internal_name||' '||
                           l_flex_value_list(i).display_name);
            END LOOP;
          END;
    END;
    -------OUTPUT
    SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE NO ENCRYPTION No Encryption
    SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE FDE BASE FDE Base
    SEAEGO_CC_FAMILYMODEL_INFO_ENCRYPTION_TYPE FDE FIPS 140-2 FDE FIPS 140-2

  • Error during runtime when using BULK collect

    Hi
    CREATE OR REPLACE TYPE VAR_ROW AS OBJECT
      VAR_1 VARCHAR2(20),
      VAR_2 VARCHAR2(20)
    CREATE OR REPLACE TYPE V_TAB AS table of VAR_ROW
    CREATE OR REPLACE FUNCTION get_id RETURN var_tbl
      PIPELINED AS
      /* declaration */
      i INTEGER;
      --TYPE x_t IS TABLE OF VAR_ROW;
      x_t   V_TAB;
      c_wip sys_refCursor;
    BEGIN
      c_wip := getTable('abc');  --the result return: 10 rows with 2 columns.
      LOOP
        FETCH c_wip BULK COLLECT
          INTO x_t LIMIT 20;  <-- error seems to come from this line
        FOR i IN x_t.FIRST .. x_t.LAST LOOP
          PIPE ROW(x_t(i).VAR_1);
          --PIPE ROW(x(i).VAR_1);
        END LOOP;
        exit when c_wip%NOTFOUND;
      end loop;
      RETURN;
    END;
    SELECT *
    FROM TABLE(get_id)
    Error msg: ORA-00932:inconsistent datatypes:expected UDT got CHARWhat could be the cause of the error?

    In addition, if your cursor returns two separate columns, you'll have to call an appropriate object constructor to create a VAR_ROW object from the two columns. The easiest solution would be to do this in your getTable method. Where you have
    SELECT column1, column2in getTable today, you'd want
    SELECT var_row( column1, column2 )Otherwise, Oracle cannot construct the VAR_ROW object from the cursor, so you cannot bulk collect it into an array of VAR_ROW.
    Justin
    Distributed Database Consulting, Inc.
    http://www.ddbcinc.com/askDDBC

  • SAVE EXCEPTIONS when fetching from cursors by BULK COLLECT possible?

    Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
    Hello,
    I'm using an Cursor's FETCH by BULK COLLECT INTO mydata...
    Is it possible to SAVE EXCEPTIONS like with FORALL? Or is there any other possibility to handle exceptions during bulk-fetches?
    Regards,
    Martin

    The cursor's SELECT-statement uses TO_DATE(juldat,'J')-function (for converting an julian date value to DATE), but some rows contain an invalid juldat-value (leading to ORA-01854).
    I want to handle this "rows' exceptions" like in FORALL.
    But it could also be any other (non-Oracle/self-made) function within "any" BULK instruction raising (un)wanted exceptions... how can I handle these ones?
    Martin

Maybe you are looking for

  • Need help on DATA_REMAP procedure.

    Hi All, I am using data pump APIs to perform export and import. But “data_remap” procedure is throwing exceptions. SQL*Plus: Release 11.1.0.7.0 - Production on Wed Apr 10 04:29:09 2013 Copyright (c) 1982, 2008, Oracle.  All rights reserved.+ Connecte

  • Some workstations slow to open files on 2008r2 share?

    We have been moving from Novell to Microsoft for a couple years company wide.  Until now, other than outlook, the apps were all local with the data being moved from a Novell server to a 2008R2 server.  A week ago we moved our final drive which was th

  • Using BT headset as a microphone?

    I am very new to all this "wireless" tech being of the old school where everything had a wire attached but thanks to Alexander Traud I now have a phone, headset and two computers all conversing nicely wirelesly. So, to the question. I am the aging DJ

  • Any good conferences in oracle e-business in 2007?

    Hi apps dbas, Do you know any good conferences in e-business in 2007? From there can share the experience with the other dbas and Know how the other shops deal with their systems? Thanks, Lily

  • SFE2000 and mikrotik VLANs not seen on both ends

    I am trying to interconnect SFE2000 and MTK RTB 433AH. I must say after years of working on Cisco equipment I was quite frustrated with Linksys setup options. Neither Trunk on General mode is not working. There is no diagnostic tools for this, yes I