DECLARE pe_ratio NUMBER(3,1); BEGIN DELETE FROM stats WHERE symbol = 'XYZ'; SELECT price / NVL(earnings, 0) INTO pe_ratio FROM stocks WHERE symbol = 'XYZ'; INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio); EXCEPTION WHEN ZERO_DIVIDE THEN ...
Though PL/SQL does not support continuable exceptions, you can still handle an exception for a statement, then continue with the next statement. Simply place the statement in its own sub-block with its own exception handlers. If an error occurs in the sub-block, a local handler can catch the exception. When the sub-block terminates, the enclosing block continues to execute at the point where the sub-block ends. Consider the following example:
DECLARE pe_ratio NUMBER(3,1); BEGIN DELETE FROM stats WHERE symbol = 'XYZ'; BEGIN ---------- sub-block begins SELECT price / NVL(earnings, 0) INTO pe_ratio FROM stocks WHERE symbol = 'XYZ'; EXCEPTION WHEN ZERO_DIVIDE THEN pe_ratio := 0; END; ---------- sub-block ends INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio); EXCEPTION ...
In this example, if the SELECT INTO statement raises a ZERO_DIVIDE exception, the local handler catches it and sets pe_ratio to zero. Execution of the handler is complete, so the sub-block terminates, and execution continues with the INSERT statement.
Before starting the transaction, you mark a savepoint. If the transaction succeeds, you commit, then exit from the loop. If the transaction fails, control transfers to the exception handler, where you roll back to the savepoint undoing any changes, then try to fix the problem.
Consider the example below. When the exception handler completes, the sub-block terminates, control transfers to the LOOP statement in the enclosing block, the sub-block starts executing again, and the transaction is retried. You might want to use a FOR or WHILE loop to limit the number of tries.
DECLARE name CHAR(20); ans1 CHAR(3); ans2 CHAR(3); ans3 CHAR(3); suffix NUMBER := 1; BEGIN ... LOOP -- could be FOR i IN 1..10 LOOP to allow ten tries BEGIN -- sub-block begins SAVEPOINT start_transaction; -- mark a savepoint /* Remove rows from a table of survey results. */ DELETE FROM results WHERE answer1 = 'NO'; /* Add a survey respondent's name and answers. */ INSERT INTO results VALUES (name, ans1, ans2, ans3); -- raises DUP_VAL_ON_INDEX if two respondents -- have the same name (because there is a unique -- index on the name column) COMMIT; EXIT; EXCEPTION WHEN DUP_VAL_ON_INDEX THEN ROLLBACK TO start_transaction; -- undo changes suffix := suffix + 1; -- try to fix name := name || TO_CHAR(suffix); -- problem ... END; -- sub-block ends END LOOP; END;