I have problem with BULK COLLECT logic on Oracle 11g.
The original logic in stored procedure is:
PROCEDURE FOO(IN_FOO IN VARCHAR2) IS
BEGIN
FOR CUR IN (SELECT COL1,COL2,COL3 FROM SOME_TABLE) LOOP
INSERT INTO OTHER_TABLE (C1,C2,C3) VALUES (CUR.COL1,CUR.COL2,CUR.COL3);
UPDATE THIRD_TABLE T SET T.C_SUM = CUR.COL2 + CUR.COL3 WHERE T.C_ID = CUR.COL1);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERROR || ': ' || SQLERRM);
END FOO;
But I want to use BULK COLLECT feature.
I wrote something like that:
PROCEDURE FOO_FAST(IN_FOO IN VARCHAR2) IS
CURSOR CUR IS SELECT COL1,COL2,COL3 FROM SOME_TABLE;
TYPE RT_CUR IS TABLE OF CUR%ROWTYPE;
LT_CUR RT_CUR;
DML_EXCEPTION EXCEPTION;
PRAGMA EXCEPTION_INIT(DML_EXCEPTION, -24381);
BEGIN
OPEN CUR;
LOOP
FETCH CUR BULK COLLECT INTO LT_CUR LIMIT 1000;
EXIT WHEN LT_CUR.COUNT = 0;
BEGIN
FORALL I IN 1 .. LT_CUR.COUNT
INSERT INTO OTHER_TABLE (C1,C2,C3) VALUES (LT_CUR(I).COL1,LT_CUR(I).COL2,LT_CUR(I).COL3);
FORALL I IN 1 .. LT_CUR.COUNT
UPDATE THIRD_TABLE T SET T.C_SUM = LT_CUR(I).COL2 + LT_CUR(I).COL3 WHERE T.C_ID = LT_CUR(I).COL1);
EXCEPTION
WHEN DML_EXCEPTION THEN
FORALL I IN 1 .. SQL%BULK_EXCEPTIONS(1).ERROR_INDEX-1
UPDATE THIRD_TABLE T SET T.C_SUM = LT_CUR(I).COL2 + LT_CUR(I).COL3 WHERE T.C_ID = LT_CUR(I).COL1);
DBMS_OUTPUT.PUT_LINE(SQLERRM(-SQL%BULK_EXCEPTIONS(1).ERROR_CODE));
RETURN;
END;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERROR || ': ' || SQLERRM);
END FOO_FAST;
Is this good approach for this problem?
What if I have more DML to perform?
Ok. My problem is more complex but I wanted to simplify it and enrich it with nice sample codes. Error OTHERS handing is not part of this problem. Maybe this will be more clear:
How this:
FOR CUR IN (SELECT COL1,COL2,COL3 FROM SOME_TABLE) LOOP
INSERT INTO OTHER_TABLE (C1,C2,C3) VALUES (CUR.COL1,CUR.COL2,CUR.COL3);
UPDATE THIRD_TABLE T SET T.C_SUM = CUR.COL2 + CUR.COL3 WHERE T.C_ID = CUR.COL1);
END LOOP;
change to BULK COLLECT and FORALL statements ?
Whether something is a "good approach" is very subjective-- it depends on what you are trying to compare against.
If we assume that your query against some_table has no predicate, it would almost certainly be more efficient (in addition to much less code) to work in sets rather than doing any sort of looping
PROCEDURE FOO(IN_FOO IN VARCHAR2) IS
BEGIN
INSERT INTO other_table( c1, c2, c3 )
SELECT col1, col2, col3
FROM some_table;
UPDATE third_table tt
SET tt.c_sum = (SELECT st.col2 + st.col3
FROM some_table st
WHERE tt.c_id = st.col1)
WHERE EXISTS( SELECT 1
FROM some_table st
WHERE tt.c_id = st.col1);
END;
Generally, a WHEN OTHERS exception handler is a bad idea. Catching an exception only to attempt to write it to DBMS_OUTPUT where the caller will have no idea that an error occurred, where the error stack is lost, and where there is no guarantee that the calling application has even allocated a buffer for the data to be written to is a bug waiting to happen. If you have this sort of code in your system, you will inevitably end up chasing hard to reproduce bugs because some piece of code somewhere encountered and swallowed an exception causing later bits of code to fail in unexpected ways.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With