The ``++FETCH ... INTO++`` statement is inefficient when used in a loop (where many records are expected). It leads to many context-switches between the SQL and PL/SQL engines. Instead, the ``++FETCH ... BULK COLLECT INTO++`` statement will issue the SQL requests in bulk, minimizing context switches.


== Noncompliant Code Example

----
SET SERVEROUTPUT ON

CREATE TABLE largeTable AS SELECT ROWNUM AS id FROM all_objects;

SET TIMING ON
DECLARE
  x PLS_INTEGER;
  CURSOR largeCursor IS SELECT ROWNUM FROM largeTable;
  largeTableRowId BINARY_INTEGER;
BEGIN
  OPEN largeCursor;

  x := 0;
  LOOP
    FETCH largeCursor INTO largeTableRowId; -- Noncompliant
    EXIT WHEN largeCursor%NOTFOUND;

    x := x + largeTableRowId;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('Sum of rownums using alternative 1: ' || x);

  CLOSE largeCursor;
END;
/
SET TIMING OFF

DECLARE
  r largeTable%ROWTYPE;
  CURSOR myCursor IS SELECT * FROM largeTable;
BEGIN
  OPEN myCursor;
  FETCH myCursor INTO r; -- Compliant, outside of a loop
  CLOSE myCursor;
END;
/

DROP TABLE largeTable;
----


== Compliant Solution

----
SET SERVEROUTPUT ON

CREATE TABLE largeTable AS SELECT ROWNUM AS id FROM all_objects;

SET TIMING ON
DECLARE
  x PLS_INTEGER;
  CURSOR largeCursor IS SELECT * FROM largeTable;
  TYPE largeTableRowIdArrayType IS TABLE OF BINARY_INTEGER INDEX BY BINARY_INTEGER;
  largeTableRowIdArray largeTableRowIdArrayType;
BEGIN
  OPEN largeCursor;

  x := 0;
  LOOP
    FETCH largeCursor BULK COLLECT INTO largeTableRowIdArray LIMIT 1000; -- Compliant

    FOR i IN largeTableRowIdArray.FIRST .. largeTableRowIdArray.LAST LOOP
      x := x + largeTableRowIdArray(i);
    END LOOP;

    EXIT WHEN largeCursor%NOTFOUND;
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('Sum of rownums using alternative 2: ' || x);

  CLOSE largeCursor;
END;
/
SET TIMING OFF

DECLARE
  r largeTable%ROWTYPE;
  CURSOR myCursor IS SELECT * FROM largeTable;
BEGIN
  OPEN myCursor;
  FETCH myCursor INTO r; -- Compliant, outside of a loop
  CLOSE myCursor;
END;
/

DROP TABLE largeTable;
----

