Skip to content

Move unnecessary statements out of Loops

Sometimes it may be necessary to obtain additional information via method calls or cursors while iterating through a loop to perform a certain task. It is always a good practice to consider the fetching of additional information is really necessary to be done for each and every iteration. Is it possible to minimize the number of method calls or OPEN-FETCH-CLOSE of cursors ?

Example

Invoice and invoiceItem Lus has a master-detail relationship. One invoice can contain many invoice items.

Consider the following procedure where, the invoice items belonging to a single invoice, is iterated over and some task is performed. To do the task, additional information from invoice Lu (master) is needed and fetched via a method call Invoice_API.Get_Adv_Inv. This is done inside the loop, but the information returned is common for every invoice item iterated over. The method call causes a performance overhead which can be minimized. The time taken to retrieve data via the method once, is multiplied by the number of iterations, and it will contribute to the actual execution time of the procedure.

PROCEDURE Unnecessary_Statements (
   company_    IN VARCHAR2,
   invoice_id_ IN NUMBER )
IS
   adv_invoice_  VARCHAR2(5);

   CURSOR get_invoice_items IS
      SELECT *
      FROM invoice_item_tab it
      WHERE it.company    = company_
      AND   it.invoice_id = invoice_id_;
BEGIN
   FOR rec_ IN get_invoice_items LOOP
      -- Method call will return the same information for each row
      -- Since company, invoice_id is common for each invoice_item
      adv_invoice_ := Invoice_API.Get_Adv_Inv(rec_.company,
                                              rec_.invoice_id); 
      -- Do something using the adv_invoice information
   END LOOP;
END Unnecessary_Statements;

Solution

Since the information is common for all invoice items, the method call can be placed outside the loop as shown below. It is obvious that the number of executions of the method call is reduced to one, irrespective of the number of invoice items, the invoice contains and thus reduces the execution time.

PROCEDURE Statements_Out_Of_Loop (
   company_    IN VARCHAR2,
   invoice_id_ IN NUMBER )
IS
   adv_invoice_  VARCHAR2(5);

   CURSOR get_invoice_items IS
      SELECT *
      FROM invoice_item_tab it
      WHERE it.company    = company_
      AND   it.invoice_id = invoice_id_;
BEGIN
   -- Method call placed outside the loop
   adv_invoice_ := Invoice_API.Get_Adv_Inv(company_,
                                           invoice_id_);                                           
   FOR rec_ IN get_invoice_items LOOP
      -- Do something using the adv_invoice information 
   END LOOP;
END Statements_Out_Of_Loop;