tags:

views:

190

answers:

3

I've been somewhat haphazardly grouping subsections of code in BEGIN...END blocks when it seems right. Mostly when I'm working on a longer stored procedure and there's a need for a temporary variable in one spot I'll declare it just for that portion of the code. I also do this when I want to identify and handle exceptions thrown for a specific piece of code.

Any other reasons why one should nest blocks within a procedure, function or another larger block of PL/SQL?

+1  A: 

I tend to nest blocks when I want to create procedures that are specific to data that only exists within the block. Here is a contrived example:

BEGIN
  FOR customer IN customers LOOP
    DECLARE

      PROCEDURE create_invoice(description VARCHAR2, amount NUMBER) IS
      BEGIN
        some_complicated_customer_package.create_invoice(
            customer_id => customer.customer_id,
            description => description,
            amount => amount
          );
      END;

    BEGIN

      /* All three calls are being applied to the current customer,
         even if we're not explicitly passing customer_id.
       */
      create_invoice('Telephone bill',  150.00);
      create_invoice('Internet bill',   550.75);
      create_invoice('Television bill', 560.45);

    END;
  END LOOP;
END;

Granted, it's not usually necessary, but it has come in really handy when a procedure can be called from many locations.

Adam Paynter
I don't see the need - you're already within a loop. Secondly, shouldn't the stored procedure declaration occur outside the loop?
OMG Ponies
@OMG Ponies: The purpose of this example was to illustrate the fact that it's possible to declare procedures within a block to take advantage of that block's data without having to pass the information via parameters. You're correct, if you want to explicitly pass `customer_id` as a parameter to `create_invoice`, it would have to be declared outside the loop. In my experience, I have run into situations where it's clearer to take advantage of the block's information.
Adam Paynter
+7  A: 

When you want to handle exceptions locally like this:

begin
   for emp_rec in (select * from emp) loop
      begin
         my_proc (emp_rec);
      exception
         when some_exception then
            log_error('Failed to process employee '||emp_rec.empno);
      end;
   end loop;
end;

In this example, the exception is handled and then we carry on and process the next employee.

Another use is to declare local variables that have limited scope like this:

declare
    l_var1 integer;
    -- lots of variables
begin
   -- lots of lines of code
   ...
   for emp_rec in (select * from emp) loop
      declare
         l_localvar integer := 0;
      begin
         -- Use l_localvar
         ...
      end
   end loop;

end;

Mind you, wanting to do this is often a sign that your program is too big and should be broken up:

declare
   l_var1 integer;
   -- lots of variables
   ...
   procedure local_proc (emp_rec emp%rowtype):
      l_localvar integer := 0;
   begin
      -- Use l_localvar
      ...
   end
begin
   -- lots of lines of code
   ...
   for emp_rec in (select * from emp) loop
      local_proc (emp_rec);
   end loop;

end; 
Tony Andrews
+1. The best practice for `begin/end` blocks is anonymous blocks, named blocks (procedure/function) or to handle specific exceptions, as in the first example. Nesting a `declare` statement within a `begin/end` block I'd call a programming bug, because it introduces the possiblity of variable scope collisions, and those are a pain to debug.
Adam Musch
A: 

One reason to have nested BEGIN/END blocks is to be able to handle exceptions for a specific local section of the code and potentially continue processing if the exception is processed.

dpbradley