在程序執(zhí)行期間,可能會發(fā)生任何數(shù)量的錯(cuò)誤(在 PL/SQL 中稱為異常)。當(dāng)引發(fā)異常時(shí),程序的正常執(zhí)行會停止,并且程序的控制權(quán)會轉(zhuǎn)移到程序的錯(cuò)誤處理部分。異常可能是服務(wù)器生成的預(yù)定義錯(cuò)誤,也可能是引發(fā)用戶定義的異常的邏輯錯(cuò)誤。

用戶定義的異常從不由服務(wù)器引發(fā);它們由 RAISE 語句顯式引發(fā)。當(dāng)開發(fā)者定義的邏輯規(guī)則被破壞時(shí),將引發(fā)用戶定義的異常;邏輯規(guī)則被破壞的一個(gè)常見例子發(fā)生在對資金不足的賬戶出具支票時(shí)。嘗試對資金不足的賬戶兌現(xiàn)支票時(shí),將引發(fā)用戶定義的異常。

您可以在函數(shù)、存儲過程、包或匿名塊中定義異常。雖然不能在同一個(gè)塊中兩次聲明同一個(gè)異常,但可以在兩個(gè)不同的塊中聲明同一個(gè)異常。

在實(shí)現(xiàn)用戶定義的異常之前,必須在函數(shù)、存儲過程、包或匿名塊的聲明部分中聲明該異常。這樣,即可使用 RAISE 語句引發(fā)該異常:

DECLARE
    exception_name EXCEPTION;

BEGIN
    ...
    RAISE exception_name;
    ...
END;

exception_name 是該異常的名稱。

未處理的異常通過調(diào)用堆棧傳播回去。如果該異常仍然未處理,則最終將會報(bào)告給客戶端應(yīng)用程序。

在塊中聲明的用戶定義的異常被視為該塊的本地異常,并且是該塊內(nèi)的任何嵌套塊的全局異常。要引用位于外部塊中的異常,必須為外部塊分配一個(gè)標(biāo)簽;然后以塊名稱作為異常名稱的前綴:

block_name.exception_name

相反的是,外部塊不能引用嵌套塊中聲明的異常。

聲明的范圍僅限于在其中聲明該異常的塊。除非該異常是在包中創(chuàng)建的,并且在引用時(shí)由包名稱限定。例如,要引發(fā)一個(gè)名為 Out_of_stock 的異常(位于名為 Inventory_control 的包中),程序必須引發(fā)具有以下名稱的錯(cuò)誤:

inventory_control.out_of_stock

以下示例演示了如何在包中聲明用戶定義的異常。當(dāng)在 check_balance 中引發(fā)時(shí),用戶定義的異常不需要包限定符,因?yàn)樗c異常位于同一包中:

CREATE OR REPLACE PACKAGE ar AS
  overdrawn EXCEPTION;
  PROCEDURE check_balance(p_balance NUMBER, p_amount NUMBER);
END;

CREATE OR REPLACE PACKAGE BODY ar AS
   PROCEDURE check_balance(p_balance NUMBER, p_amount  NUMBER)
   IS
   BEGIN
       IF (p_amount > p_balance) THEN
         RAISE overdrawn;
       END IF;
    END;

以下存儲過程 (purchase) 調(diào)用 check_balance 存儲過程。如果 p_amount 大于 p_balance,則 check_balance 會引發(fā)異常;purchase 會捕獲 ar.overdrawn 異常。purchase 必須使用包限定名稱 (ar.overdrawn) 來引用異常,因?yàn)?purchase 未在 ar 包中定義。

CREATE PROCEDURE purchase(customerID INT, amount NUMERIC)
AS
  BEGIN
     ar.check_ balance(getcustomerbalance(customerid), amount);
       record_purchase(customerid, amount);
  EXCEPTION
     WHEN ar.overdrawn THEN
       raise_credit_limit(customerid, amount*1.5);
  END;

當(dāng) ar.check_balance 引發(fā)異常時(shí),執(zhí)行會跳到 purchase 中定義的異常處理程序:

EXCEPTION
     WHEN ar.overdrawn THEN
       raise_credit_limit(customerid, amount*1.5);

該異常處理程序會提高客戶的信用額度,然后結(jié)束。當(dāng)異常處理程序結(jié)束時(shí),執(zhí)行會從 ar.check_balance 后面的語句恢復(fù)。