PL/SQL Exception Example

一个PL/SQL程序的错误可以分为两种:编译时刻错误、运行时刻错误。

 

编译时刻错误

由于用户输入(如表名拼写错误)、能力(语法格式错误)等方面的原因而造成的程序方面的错误就是编译时的错误。这些错误在编译时会被PL/SQL编译器发现。也可以查询数据字典USER_ERRORS、ALL_ERRORS、DBA_ERRORS来读取错误信息。

 

运行时刻错误

即便是通过了编译的PL/SQL程序在运行时刻还是可能产生错误的。导致运行时错误的原因很多,如内存用尽、硬件故障、违反表的完整性约束、设计上的缺陷、被0除、数字或值在长度大小方面不匹配等。

 

在程序内部不需要对编译时错误做特殊处理。而运行时错误可能随着运行环境的变化而随时出现,难以预防、检查、排除。所以为了提高程序的健壮性、处理潜在的错误,开发人员必须在程序中尽可能地考虑各种主要异常错误,并进行相应的处理。

 

异常分为系统预定义、非预定义、用户定义三种。另外,异常分为错误和非错误两种。系统预定义、非预定义的异常都是针对Oracle(发现的)错误的,而用户定义的异常一般用于处理非错误的异常。

 

 

预定义异常错误

可以查看${ORACLE_HOME}\RDBMS\ADMIN\stdspec.sql脚本文件可以找到这些预定义异常错误。

预定义异常错误虽然是被隐含引发的,但要在异常处理部分利用“异常处理名称”来处理相应的异常错误。

 

--NO_DATA_FOUND

DECLARE
   v_ename   emp.ename%TYPE;
   v_empno   emp.empno%TYPE;
BEGIN
   v_empno := &eno;

   SELECT ename
     INTO v_ename
     FROM emp
    WHERE empno = v_empno;

   DBMS_OUTPUT.put_line (
      '雇员编号:' || v_empno || ',雇员名称:' || v_ename);
EXCEPTION
   WHEN NO_DATA_FOUND
   THEN
      DBMS_OUTPUT.put_line (
         '雇员编号:' || v_empno || ',不存在,请重新输入');
END;
/

 

--CASE_NOT_FOUND

DECLARE
   v_grade   CHAR := 'D';
BEGIN
   CASE v_grade
      WHEN 'A'
      THEN
         DBMS_OUTPUT.put_line ('Excellent');
      WHEN 'B'
      THEN
         DBMS_OUTPUT.put_line ('Very good');
      WHEN 'C'
      THEN
         DBMS_OUTPUT.put_line ('Good');
   END CASE;
EXCEPTION
   WHEN case_not_found
   THEN
      DBMS_OUTPUT.put_line ('No such grade');
      DBMS_OUTPUT.put_line (
         '在CASE语句中缺少与 ' || 'D' || ' 相关的条件');
END;
/
 

--DUP_VAL_ON_INDEX

DECLARE
   v_dept   dept%ROWTYPE;
BEGIN
   INSERT INTO dept
        VALUES (40, '印刷厂', '北京海淀区');
EXCEPTION
   WHEN DUP_VAL_ON_INDEX
   THEN
      DBMS_OUTPUT.put_line (
         '违反了主键(部门编号)不能重复的实体完整性约束');
END;
/
 

--TOO_MANY_ROWS

DECLARE
   v_ename   emp.ename%TYPE;
BEGIN
   SELECT ename
     INTO v_ename
     FROM emp
    WHERE sal = &sal;
EXCEPTION
   WHEN TOO_MANY_ROWS
   THEN
      DBMS_OUTPUT.put_line (
         '返回了多行,请使用游标来处理多行记录的集合');
END;
/
 

--OTHERS

BEGIN
   DBMS_OUTPUT.put_line (1 / 0);                            --会发生ZERO_DIVIDE异常
EXCEPTION
   WHEN DUP_VAL_ON_INDEX
   THEN
      DBMS_OUTPUT.put_line (
         '违反了主键(部门编号)不能重复的实体完整性约束');
   WHEN TOO_MANY_ROWS
   THEN
      DBMS_OUTPUT.put_line (
         '返回了多行,请使用游标来处理多行记录的集合');
   WHEN OTHERS
   THEN
      DBMS_OUTPUT.put_line ('发生了其他异常');
END;
/

 

非预定义异常错误

--为错误代码为“-1400”的Oracle错误声明异常,并进行处理

DECLARE
   ept_null_error   EXCEPTION;
   PRAGMA EXCEPTION_INIT (ept_null_error, -1400);
BEGIN
   INSERT INTO dept
        VALUES (NULL, '印刷厂', '北京海淀区');
EXCEPTION
   WHEN ept_null_error
   THEN
      DBMS_OUTPUT.put_line ('不能向dept表的"部门编号"列插入NULL');
END;
/

 

自定义异常

预定义异常、非预定义异常都是有Oracle判断的异常错误。而在实际的PL/SQL程序开发过程中,为了实施具体的业务规则、编程和调试的需要,程序员可以自定义一些异常。他们往往并不一定是个错误,也不会对数据库造成破坏性的影响,而是程序员为特殊情况所定义的异常,以便将程序的控制流程引向异常处理部分,利用异常处理部分来集中处理这些问题。这种方法使程序的结果比较完美。

自定义异常必须要声明,并且必须要用RAISE语句显示引发(或抛出),然后才能将程序的控制流程引向异常处理部分。

 

--用异常处理的方法来补充提示雇员的工资太高了

DECLARE
   v_empno   emp.empno%TYPE;
   v_sal     emp.sal%TYPE;
   ept_sal   EXCEPTION;
BEGIN
   v_empno := &eno;

   SELECT sal
     INTO v_sal
     FROM emp
    WHERE empno = v_empno;

   IF v_sal >= 4000
   THEN
      DBMS_OUTPUT.put_line (v_empno || ' 雇员的工资是 ' || v_sal);
      RAISE ept_sal;
   ELSE
      DBMS_OUTPUT.put_line (
         v_empno || ' 雇员的工资是 ' || v_sal || ',基本正常');
   END IF;
EXCEPTION
   WHEN ept_sal
   THEN
      DBMS_OUTPUT.put_line (
         '补充:该雇员的工资超过4000了,太高了');
END;
/
 

使用SQLCODE和SQLERRM

另一种处理异常(预定义、尤其是非预定义异常)的方法是在异常处理部分最后的WHEN OTHERS子句使用内置异常函数SQLCODE和SQLERRM,以便根据SQL语句执行后的状态,即错误代码和错误消息,决定处理方法。

SQLCODE函数没有参数,它返回Oracle的错误代码的数字。

SQLERRM(oracle_error_code)函数按照输入的Oracle错误代码oracle_error_code返回其对应的Oracle错误消息文本。当省略oracle_error_code时,SQLERRM函数返回SQLCODE当前值对应的错误消息文本。

 

--几个特殊错误代码时SQLERRM函数的返回值

BEGIN
   DBMS_OUTPUT.put_line ('SQLERRM(0): ' || SQLERRM (0));
   DBMS_OUTPUT.put_line ('SQLERRM(100): ' || SQLERRM (100));
   DBMS_OUTPUT.put_line ('SQLERRM(10): ' || SQLERRM (10));
   DBMS_OUTPUT.put_line ('SQLERRM(1): ' || SQLERRM (1));
   DBMS_OUTPUT.put_line ('SQLERRM: ' || SQLERRM);
   DBMS_OUTPUT.put_line ('SQLERRM(-1): ' || SQLERRM (-1));
   DBMS_OUTPUT.put_line ('SQLERRM(-54): ' || SQLERRM (-54));
END;
/

SQLCODE和SQLERRM函数均不能直接用在SQL语句中,如果需要在SQL语句中使用的话,需要先将其值赋予变量,然后再引用。

 

--用SQLCODE和SQLERRM函数来处理非预定义异常错误

BEGIN
   INSERT INTO dept
        VALUES (NULL, '印刷厂', '北京海淀区');
EXCEPTION
   WHEN NO_DATA_FOUND
   THEN
      DBMS_OUTPUT.put_line ('没有找到数据');
   WHEN OTHERS
   THEN
      DBMS_OUTPUT.put_line (SQLCODE || '、' || SQLERRM);

      CASE SQLCODE
         WHEN -1400
         THEN
            DBMS_OUTPUT.put_line ('不能向非空字段插入NULL值');
         WHEN -2291
         THEN
            DBMS_OUTPUT.put_line ('主表中没有主键值,不能更新');
         WHEN -2292
         THEN
            DBMS_OUTPUT.put_line ('还有子记录,不能删除父记录');
      END CASE;
END;
/
 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值