Excepţii

Structura unui bloc:

DECLARE, sau definiţie subprogram (procedură, funcţie)

[declaraţii de variabile, constante, cursoare, excepţii, tipuri, proceduri locale, funcţii locale, etc.]
BEGIN
  instrucţiuni
  [EXCEPTION
    tratare excepţii
  ]
END;

Un bloc anonim sau un subprogram se poate termina în mod normal, când se execută toate instrucţiunile, sau la apariţia unei erori (excepţie). O eroare apărută poate fi tratată:

La apariţia unei erori (excepţii) se trece controlul la secţiunea EXCEPTION a blocului unde aceasta a apărut. Asocierea de acţiuni la excepţii se face în această secţiune prin precizarea unui şir de instrucţiuni ce se execută la apariţia erorii (erorile sunt identificate printr-o denumire):
WHEN identificare_exceptie THEN lista_de_instructiuni;
Excepţiile netratate până la un moment dat pot fi luate împreună pentru o tratare unică. Acestea vor fi precizate prin identificarea OTHERS din clauza WHEN.
Dacă într-un bloc apare o eroare, atunci se caută modul de tratare a ei în blocul curent. Dacă se găseşte, atunci blocul curent se termină şi se continuă blocul care-l include pe cel curent, din blocul curent instrucţiunile rămase după apariţia erorii sunt abandonate.
Dacă eroarea nu poate fi tratată în blocul curent, atunci se caută tratarea în blocurile ce includ blocul curent, până se poate trata sau se termină şi blocul de bază. Trecerea de la un bloc la precedentul duce la abandonarea instrucţiunilor ne-executate până la apariţia erorii. Dacă nici în blocul de bază nu se poate trata eroarea, atunci blocul se termină cu eroarea 'Unhandled'.
Există două funcţii PL/SQL ce se pot folosi pentru identificarea erorilor, utile mai ales dacă sunt tratate prin WHEN OTHERS:

Excepţii interne

Excepţiile interne sistemului Oracle apar când nu e respectată o restricţie a sistemului.
O excepţie internă are asociată un număr (cod), iar unele dintre ele au şi o denumire.

Exemple:

Denumirea excepţieiCodul OracleObservaţii
CURSOR_ALREADY_OPENORA-06511 
DUP_VAL_ON_INDEXORA-00001 
LOGIN_DENIEDORA-01017 
VALUE_ERRORORA-06502la trunchieri sau conversii, ex:
x varchar(5);
x:='0123456';
ZERO_DEVIDEORA-01476 
INVALID_NUMBERORA-01722se face referire la o construcţie care nu este număr, ex:
to_char('abc');
NO_DATA_FOUNDORA-01403 
TOO_MANY_ROWSORA-01422 

Excepţii utilizator

Utilizatorii pot să-şi definească excepţii proprii prin definirea unui identificator de tip EXCEPTION în partea de declaraţii a blocului:
identificator EXCEPTION;
Atunci când sunt întrunite condiţiile dorite pentru o excepţie (acea excepţie pentru care s-a definit identificatorul) se poate genera această excepţie prin instrucţiunea:
RAISE identificator;
Excepţia definită de utilizator poate fi tratată în partea de excepţii a blocului, la fel ca o excepţie a sistemului.

Exemple (care se pot testa în SQL*Plus sau SQL Developer)

DECLARE
  n CHAR(20);
BEGIN
  SELECT nume INTO n FROM studenti;
  -- va genera: ORA-01422: exact fetch returns more than requested number of rows 
END;
Memorarea erorilor într-un tabel:
CREATE TABLE erori(cod number, mesaj varchar(80));
 
DECLARE
  n CHAR(20);
  mesaj CHAR(80);
  cod NUMBER;
BEGIN
  SELECT nume INTO n FROM studenti;
  EXCEPTION
    WHEN OTHERS THEN
      mesaj:=SUBSTR(SQLERRM,1,80);
      cod:=SQLCODE;
      INSERT INTO erori VALUES(cod, mesaj);
END;
/
SELECT * FROM erori;
DROP TABLE erori;
CREATE TABLE erori(cod number, mesaj varchar(80));

DECLARE
  n CHAR(20);
  mesaj CHAR(80);
  cod NUMBER;
BEGIN
  SELECT nume INTO n FROM studenti;
  EXCEPTION
    WHEN TOO_MANY_ROWS THEN
      INSERT INTO erori(mesaj) VALUES('Prea multe linii');
    WHEN OTHERS THEN
      mesaj:=SUBSTR(SQLERRM,1,80);
      cod:=SQLCODE;
      INSERT INTO erori VALUES(cod, mesaj);
END;
/
SELECT * FROM erori;
DROP TABLE erori;
Calculul factorialului cu o funcţie locală.
In funcţie nu se verifică valoarea argumentului (dacă argumentul este >=0).
set serveroutput on

DECLARE
  n number;
  m number;
  i number;
  FUNCTION f(n in number) RETURN NUMBER;
  FUNCTION f(n in number) RETURN NUMBER IS
    BEGIN
     IF n=0 THEN RETURN 1;
     ELSE RETURN n*f(n-1);
     END IF;
    END;
BEGIN
  dbms_output.enable;
  n:=10;
  FOR i IN 1..n LOOP
    m:=f(i);
    dbms_output.put_line('(' || to_char(i) || ')! = ' || to_char(m));
  END LOOP i;
END;
Calculul factorialului cu o funcţie locală.
In funcţie se generează eroare pentru argument negativ.
set serveroutput on

DECLARE
  n number;
  m number;
  i number;
  FUNCTION f(n in number) RETURN NUMBER;
  FUNCTION f(n in number) RETURN NUMBER IS
    val_negativa EXCEPTION;
    BEGIN
     IF n<0 then RAISE val_negativa; END IF;
     IF n=0 THEN RETURN 1;
     ELSE RETURN n*f(n-1);
     END IF;
     EXCEPTION
       WHEN val_negativa THEN
          dbms_output.put_line('Valoare negativa');
    END;
BEGIN
  --vor rezulta erori
  dbms_output.enable;
  n:=-10;
  --n:=10;
  m:=f(n);
  dbms_output.put_line('(' || to_char(n) || ')! = ' || to_char(m));
END;
 
La folosirea funcţiei, dacă apare o eroare, atunci apare o eroare suplimentară la blocul de bază, deoarece f(...) nu are valoare.
Eliminarea erorii precedente.
set serveroutput on

DECLARE
  n number;
  m number;
  i number;
  FUNCTION f(n in number) RETURN NUMBER;
  FUNCTION f(n in number) RETURN NUMBER IS
    val_negativa EXCEPTION;
    BEGIN
     IF n<0 then RAISE val_negativa; END IF;
     IF n=0 THEN RETURN 1;
     ELSE RETURN n*f(n-1);
     END IF;
     EXCEPTION
       WHEN val_negativa THEN
          dbms_output.put_line('Valoare negativa');
    END;
BEGIN
  dbms_output.enable;
  n:=-10;
  m:=f(n);
  dbms_output.put_line('(' || to_char(n) || ')! = ' || to_char(m));
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line('Terminare eronata');
END;
Funcţie memorată pentru calculul factorialului, fără analiza excepţiilor.
CREATE OR REPLACE FUNCTION factorial (n in NUMBER) RETURN NUMBER AS
BEGIN
  IF n=0 THEN RETURN 1;
  ELSE RETURN n*factorial(n-1);
  END IF;
END;
Funcţie memorată pentru calculul factorialului, cu analiza argumentului negativ.
CREATE OR REPLACE FUNCTION factorial (n in NUMBER) RETURN NUMBER AS 
  val_negativa exception;
BEGIN
  IF n<0 THEN raise val_negativa; end if;
  IF n=0 THEN RETURN 1;
  ELSE RETURN n*factorial(n-1);
  END IF;
  EXCEPTION
    WHEN val_negativa THEN RETURN -1;
END;