在程序执行期间,可能会发生任何数量的错误(在 PL/SQL 中称为异常)。当引发异常时,程序的正常执行会停止,并且程序的控制权会转移到程序的错误处理部分。异常可能是服务器生成的预定义错误,也可能是引发用户定义的异常的逻辑错误。

用户定义的异常从不由服务器引发;它们由 RAISE 语句显式引发。当开发者定义的逻辑规则被破坏时,将引发用户定义的异常;逻辑规则被破坏的一个常见例子发生在对资金不足的账户出具支票时。尝试对资金不足的账户兑现支票时,将引发用户定义的异常。

您可以在函数、存储过程、包或匿名块中定义异常。虽然不能在同一个块中两次声明同一个异常,但可以在两个不同的块中声明同一个异常。

在实现用户定义的异常之前,必须在函数、存储过程、包或匿名块的声明部分中声明该异常。这样,即可使用 RAISE 语句引发该异常:

DECLARE
    exception_name EXCEPTION;

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

exception_name 是该异常的名称。

未处理的异常通过调用堆栈传播回去。如果该异常仍然未处理,则最终将会报告给客户端应用程序。

在块中声明的用户定义的异常被视为该块的本地异常,并且是该块内的任何嵌套块的全局异常。要引用位于外部块中的异常,必须为外部块分配一个标签;然后以块名称作为异常名称的前缀:

block_name.exception_name

相反的是,外部块不能引用嵌套块中声明的异常。

声明的范围仅限于在其中声明该异常的块。除非该异常是在包中创建的,并且在引用时由包名称限定。例如,要引发一个名为 Out_of_stock 的异常(位于名为 Inventory_control 的包中),程序必须引发具有以下名称的错误:

inventory_control.out_of_stock

以下示例演示了如何在包中声明用户定义的异常。当在 check_balance 中引发时,用户定义的异常不需要包限定符,因为它与异常位于同一包中:

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) 调用 check_balance 存储过程。如果 p_amount 大于 p_balance,则 check_balance 会引发异常;purchase 会捕获 ar.overdrawn 异常。purchase 必须使用包限定名称 (ar.overdrawn) 来引用异常,因为 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;

当 ar.check_balance 引发异常时,执行会跳到 purchase 中定义的异常处理程序:

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

该异常处理程序会提高客户的信用额度,然后结束。当异常处理程序结束时,执行会从 ar.check_balance 后面的语句恢复。