Overuse of GOTO statements can result in complex, unstructured code (sometimes called spaghetti code) that is hard to understand and maintain. So, use GOTO statements sparingly. For example, to branch from a deeply nested structure to an error-handling routine, raise an exception rather than use a GOTO statement.
BEGIN ... GOTO insert_row; ... <<insert_row>> INSERT INTO emp VALUES ... END;
In the next example, you go to a PL/SQL block farther up in a sequence of statements:
BEGIN
   ...
   <<update_row>>
   BEGIN
      UPDATE emp SET ...
      ...
   END;
   ...
   GOTO update_row;
   ...
END;The label <<end_loop>> in the following example is illegal because it does not precede an executable statement:
DECLARE
   done  BOOLEAN;
BEGIN
   ...
   FOR i IN 1..50 LOOP
      IF done THEN
         GOTO end_loop;
      END IF;
      ...
   <<end_loop>>  -- illegal
   END LOOP;  -- not an executable statement
END;To debug the last example, simply add the NULL statement, as follows:
DECLARE
   done  BOOLEAN;
BEGIN
   ...
   FOR i IN 1..50 LOOP
      IF done THEN
         GOTO end_loop;
      END IF;
      ...
   <<end_loop>>
   NULL;  -- an executable statement
   END LOOP;
END;As the following example shows, a GOTO statement can branch to an enclosing block from the current block:
DECLARE
   my_ename  CHAR(10);
BEGIN
   ...
   <<get_name>>
   SELECT ename INTO my_ename FROM emp WHERE ...
   ...
   BEGIN
      ...
      GOTO get_name;  -- branch to enclosing block
   END;
END;The GOTO statement branches to the first enclosing block in which the referenced label appears.
BEGIN
   ...
   GOTO update_row;  -- illegal branch into IF statement
   ...
   IF valid THEN
      ...
      <<update_row>>
      UPDATE emp SET ...
   END IF;
END;Also, a GOTO statement cannot branch from one IF statement clause to another, as the following example shows:
BEGIN
   ...
   IF valid THEN
      ...
      GOTO update_row;  -- illegal branch into ELSE clause
   ELSE
      ...
      <<update_row>>
      UPDATE emp SET ...
   END IF;
END;The next example shows that a GOTO statement cannot branch from an enclosing block into a sub-block:
BEGIN
   ...
   IF status = 'OBSOLETE' THEN
      GOTO delete_part;  -- illegal branch into sub-block
   END IF;
   ...
   BEGIN
      ...
      <<delete_part>>
      DELETE FROM parts WHERE ...
   END;
END;Also, a GOTO statement cannot branch out of a subprogram, as the following example shows:
DECLARE
   ...
   PROCEDURE compute_bonus (emp_id NUMBER) IS
   BEGIN
      ...
      GOTO update_row;  -- illegal branch out of subprogram
   END;
BEGIN
   ...
   <<update_row>>
   UPDATE emp SET ...
END;Finally, a GOTO statement cannot branch from an exception handler into the current block. For example, the following GOTO statement is illegal:
DECLARE
   ...
   pe_ratio  REAL;
BEGIN
   ...
   SELECT price / NVL(earnings, 0) INTO pe_ratio FROM ...
   <<insert_row>>
   INSERT INTO stats VALUES (pe_ratio, ...);
EXCEPTION
   WHEN ZERO_DIVIDE THEN
      pe_ratio := 0;
      GOTO insert_row;  -- illegal branch into current block
END;However, a GOTO statement can branch from an exception handler into an enclosing block.
EXCEPTION
   WHEN ZERO_DIVIDE THEN
      ROLLBACK;
   WHEN VALUE_ERROR THEN
      INSERT INTO errors VALUES ...
      COMMIT;
   WHEN OTHERS THEN
      NULL;
END;Each clause in an IF statement must contain at least one executable statement. The NULL statement meets this requirement. So, you can use the NULL statement in clauses that correspond to circumstances in which no action is taken. In the following example, the NULL statement emphasizes that only top-rated employees receive bonuses:
IF rating > 90 THEN compute_bonus(emp_id); ELSE NULL; END IF;
Also, the NULL statement is a handy way to create stubs when designing applications from the top down. A stub is dummy subprogram that allows you to defer the definition of a procedure or function until you test and debug the main program. In the following example, the NULL statement meets the requirement that at least one statement must appear in the executable part of a subprogram:
PROCEDURE debit_account (acct_id INTEGER, amount REAL) IS BEGIN NULL; END debit_account;