`
刘金剑
  • 浏览: 145115 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

PL\SQL用户指南与参考7.2 转载

阅读更多

八、处理PL/SQL异常

异常抛出时,PL/SQL块或子程序的正常执行就会停止,控制权转到块或子程序的异常处理部分,语法如下:

EXCEPTION
  WHEN  exception_name1 THEN    -- handler
    sequence_of_statements1
  WHEN  exception_name2 THEN    -- another handler
    sequence_of_statements2
    ...
  WHEN  OTHERS  THEN    -- optional handler
    sequence_of_statements3
END ;

为捕获抛出的异常,我们需要编写异常处理程序。每个处理程序都由一个WHEN子句和语句序列组成。这些语句执行完毕后,块或子程序就会结束,控制权不再返回异常被抛起的地方。换句话说,也就是我们不能再次返回异常发生的地方继续执行我们的程序。

可选的OTHERS处理器总是块或子程序的最后一个处理程序,它可以用于捕获所有的未命名异常。因此,块或子程序只能有一个OTHERS处理器。如下例所示,OTHERS处理器能够保证所有的异常都会被控制:

EXCEPTION
  WHEN  ... THEN
    -- handle the error
  WHEN  ... THEN
    -- handle the error
  WHEN  OTHERS  THEN
    -- handle all other errors
END ;

如果我们想让两个或更多的异常执行同样的语句序列,只需把异常名称用关键字OR隔开,放在同一个WHEN子句中即可,如下例所示:

EXCEPTION
  WHEN  over_limit OR  under_limit OR  VALUE_ERROR THEN
  -- handle the error

只要在WHEN子句的异常列表中有一项与被抛出异常相匹配,相关的语句序列就会被执行。关键字OTHERS不能出现在异常名称列表中;它只能单独使用。我们可以有任意数量的异常处理程序,而且每个处理程序都与一个异常列表及其对应的语句序列相关联。但是,异常名称只能在块或子程序的异常处理部分出现一次。

变量作用范围的规则在这里也同样适用,所以我们可以在异常处理程序中引用本地或全局变量。但是,当游标FOR循环中有异常抛出时,游标就会在异常处理程序调用之前被隐式地关闭。因此,显式游标的属性值在异常处理程序中就不再可用了。

1、声明中控制异常

如果在声明时使用了错误的初始化表达式也有可能引发异常。例如,下面的声明就是因常量credit_limit不能存储超过999的数字而抛出了异常:

DECLARE
  credit_limit CONSTANT  NUMBER (3) := 5000;   -- raises an exception
  BEGIN
  ...
EXCEPTION
  WHEN  OTHERS  THEN    -- cannot catch the exception
  ...
END ;

当前块中的处理程序并不能捕获到抛出的异常,这是因为声明时抛出的异常会被立即传递到最近的封闭块中去。

2、异常句柄中控制异常

在一个块或子程序中,一次只能有一个异常被激活。所以,一个被异常处理程序抛出的异常会被立即传递到封闭块,在那儿,封闭块会为它查找新的处理程序。从那一刻起,异常传递才开始正常化。参考下面的例子:

EXCEPTION
  WHEN  INVALID_NUMBER THEN
    INSERT  INTO  ...   -- might raise DUP_VAL_ON_INDEX
  WHEN  DUP_VAL_ON_INDEX THEN  ...   -- cannot catch the exception
END ;

3、异常分支

GOTO语句不能跳转到异常控制程序。同样,GOTO语句也不能从异常控制程序跳转到当前块。例如,下面的GOTO语句就是非法的:

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' ;

  <<my_label>>
  INSERT  INTO  stats (symbol, ratio)
       VALUES  ('xyz' , pe_ratio);
EXCEPTION
  WHEN  ZERO_DIVIDE THEN
    pe_ratio  := 0;
    GOTO  my_label;   -- illegal branch into current block
END ;

但是,GOTO语句可以从一个异常控制程序中跳转到一个封闭块。

4、获取错误代号与消息:SQLCODE和SQLERRM

在异常处理程序中,我们可以使用内置函数SQLCODE和SQLERRM来查出到底发生了什么错误,并能够获取相关的错误信息。对于内部异常来说, SQLCODE会返回Oracle错误编号。SQLCODE返回的总是一个负数,除非发生的Oracle错误是没有找到数据,这时返回的是+100。 SQLERRM会返回对应的错误消息。消息是以Oracle错误编号开头的。

如果我们没有使用编译指令EXCEPTION_INIT把异常与编号关联的话,SQLCODE和SQLERRM就会分别返回+1和消息"User- Defined Exception"。Oracle错误消息最大长度是512个字符,其中包括错误编号、嵌套消息和具体表和字段的名称。

如果没有异常抛出,SQLCODE返回0,SQLERRM返回消息"ORA-0000: normal, successful completion"。

我们可以把错误编号传递给SQLERRM,让它返回对应的错误消息。但是,一定要保证我们传递给SQLERRM的错误编号是负数。下例中,我们把一个正数传递给SQLERRM,结果就不是我们想要的那样的了:

DECLARE
  err_msg   VARCHAR2 (100);
BEGIN
  /* Get all Oracle error messages. */
  FOR  err_num IN  1 .. 9999 LOOP
    err_msg    := SQLERRM (err_num);   -- wrong; should be -err_num

    INSERT  INTO  ERRORS
         VALUES  (err_msg);
  END  LOOP ;
END ;

把正数传给SQLERRM时,如果传递的是+100,返回的结果是"no data found",其他情况总是会返回消息"user-defined exception"。把0传递给SQLERRM,就会返回消息"normal, successful completion"。

我们不能直接在SQL语句中使用SQLCODE或SQLERRM。我们必须先把它们的值赋给本地变量,然后再在SQL中使用变量,如下例所示:

DECLARE
  err_num   NUMBER ;
  err_msg   VARCHAR2 (100);
BEGIN
  ...
EXCEPTION
  WHEN  OTHERS  THEN
    err_num    := SQLCODE ;
    err_msg    := SUBSTR(SQLERRM , 1, 100);

    INSERT  INTO  ERRORS
         VALUES  (err_num, err_msg);
END ;

字符串函数SUBSTR可以保证用SQLERRM为err_msg赋值时不会引起VALUE_ERROR异常。函数SQLCODE和SQLERRM在OTHERS异常处理程序中特别有用,因为它们能让我们知道哪个内部异常被抛出。

注意:在使用编译指示RESTRICT_REFERENCES判断存储函数的纯度时,如果函数调用了SQLCODE和SQLERRM,我们就不能指定约束为WNPS和RNPS了。

5、捕获未控制异常

记住,如果被抛出的异常找不到合适的异常控制程序,PL/SQL会向主环境抛出一个未捕获的异常错误,然后由主环境决定如何处理。例如,在Oracle预编译程序环境中,任何一个执行失败的SQL语句或PL/SQL块所涉及到的改动都会被回滚。

未捕获也能影响到子程序。如果我们成功地从子程序中退出,PL/SQL就会把值赋给OUT参数。但是,如果我们因未捕获异常而退出程序,PL/SQL就不会为OUT参数进行赋值。同样,如果一个存储子程序因异常而执行失败,PL/SQL也不会回滚子程序所做的数据变化。

我们可以在每个PL/SQL程序的顶级使用OTHERS句柄来捕获那些没有被子程序捕捉到的异常。

九、PL/SQL错误控制技巧

这里,我们将学习三个提高程序灵活性的技巧。

1、模拟TRY..CATCH..块

异常控制程序能让我们在退出一个块之前做一些恢复操作。但是在异常程序完成后,语句块就会终止。我们不能从异常句柄再重新回到当前块。例如,如果下面的SELECT INTO语句引起了ZERO_DIVIDE异常,我们就不能执行INSERT语句了:

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
    ...
END ;

其实我们可以控制某一条语句引起的异常,然后继续下一条语句。只要把可能引起异常的语句放到它自己的子块中,并编写对应的异常控制程序。一旦在子块中有错误发生,它的本地异常处理程序就能捕获并处理异常。当子块结束时,封闭块程序会继续执行紧接着的下一条语句。如下例:

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
  WHEN  OTHERS  THEN
    ...
END ;

在上面这个例子中,如果SELECT INTO语句抛出了ZERO_DIVIDE异常,本地异常处理程序就会捕捉到它并把pe_ratio赋值为0。当处理程序完成时,子块也就终止,INSERT语句就会被执行。

2、反复执行的事务

异常发生后,我们也许还不想放弃我们事务,仍想重新尝试一次。这项技术的实现方法就是:

  1. 把事务装入一个子块中。
  2. 把子块放入一个循环,然后反复执行事务
  3. 在开始事务之前标记一个保存点。如果事务执行成功的话,就提交事务并退出循环。如果事务执行失败,控制权就会交给异常处理程序,事务回滚到保存点,然后重新尝试执行事务。

如下例所示。当异常处理程序完成时,子块终止,控制权被交给外围块的LOOP语句,子块再次重新开始执行。而且,我们还可以用FOR或WHILE语句来限制重做的次数。

DECLARE
  NAME     VARCHAR2 (20);
  ans1     VARCHAR2 (3);
  ans2     VARCHAR2 (3);
  ans3     VARCHAR2 (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
      COMMIT ;
      EXIT ;
    EXCEPTION
      WHEN  DUP_VAL_ON_INDEX THEN
        ROLLBACK  TO  start_transaction;   -- undo changes
        suffix    := suffix + 1;   -- try to fix problem
        NAME      := NAME || TO_CHAR(suffix);
    END ;   -- sub-block ends
  END  LOOP ;
END ;

3、使用定位变量标记异常发生点

只用一个异常句柄来捕获一系列语句的话,可能无法知道到底是哪一条语句产生了错误:

BEGIN
  SELECT  ...
  SELECT  ...
EXCEPTION
  WHEN  NO_DATA_FOUND THEN  ...
  -- Which SELECT statement caused the error?
END ;

要想解决这个问题,我们可以使用一个定位变量来跟踪执行语句,例如:

DECLARE
  stmt INTEGER  := 1;   -- designates 1st SELECT statement
BEGIN
  SELECT  ...
  stmt := 2;   -- designates 2nd SELECT statement
  SELECT  ...
EXCEPTION
  WHEN  NO_DATA_FOUND THEN
    INSERT  INTO  errors VALUES  ('Error in statement '  || stmt);
END ;

分享到:
评论

相关推荐

    PLSQL Developer用户指南

    4 PL/SQL Developer 7.0 用户指南 6.6 更新数据库 .......43 6.7 查看和编辑 XMLTYPE 列 .........44 6.8 直接查询导出 .....44 6.9 保存 SQL 脚本 .....44 6.10 创建标准查询 .....45 7. 命令窗口.......46 7.1 ...

    pl/sql操作手册

    4 PL/SQL Developer 7.0 用户指南 6.6 更新数据库....................................................43 6.7 查看和编辑 XMLTYPE 列..........................................44 6.8 直接查询导出................

    精通Oracle.10g.PLSQL编程

    编写控制结构 7.1 条件分支语句 7.2 CASE语句 7.3 循环语句 7.4 顺序控制语句 7.5 习题 第8章 使用复合数据类型 8.1 PL/SQL记录 8.1.1 定义PL/SQL记录 8.1.2 使用PL/SQL...

    oracle数据库11G初学者指南.Oracle.Database.11g,.A.Beginner's.Guide

    7.2 0racle用户管理的备份和恢复 7.2.1 用户管理的备份类型 7.2.2 冷备份 7.2.3 热备份 7.2.4 从冷备份中恢复 7.2.5 从热备份中恢复 7.2.6 恢复的7个步骤 7.2.7 用备份控制文件进行恢复 7.3 编写数据库备份脚本 7.4 ...

    Oracle Database 11g初学者指南--详细书签版

    7.2 Oracle用户管理的备份和恢复 183 7.2.1 用户管理的备份类型 184 7.2.2 冷备份 184 7.2.3 热备份 184 7.2.4 从冷备份中恢复 185 7.2.5 从热备份中恢复 186 7.2.6 恢复的7个步骤 187 7.2.7 用备份控制文件...

    Oracle_Database_11g完全参考手册.part2/3

    《Oracle Database 11g完全参考手册》全面详细地介绍了Oracle Database 11g的强大功能,阐述了如何使用所有的新增功能和工具,如何执行功能强大的SOL查询,如何编写PL/SQL和SQL*Plus语句,如何使用大对象和对象,...

    Oracle_Database_11g完全参考手册.part3/3

    《Oracle Database 11g完全参考手册》全面详细地介绍了Oracle Database 11g的强大功能,阐述了如何使用所有的新增功能和工具,如何执行功能强大的SOL查询,如何编写PL/SQL和SQL*Plus语句,如何使用大对象和对象,...

    PLSQL_Developer_9.0用户指南

    4 PL/SQL Developer 9.0 用户指南 7.8 直接查询导出 ......................................................... 45 7.9 保存 SQL 脚本 ........................................................ 45 7.10 创建...

    数据库基础

    §11.2 SQL与PL/SQL 231 §11.2.1 什么是PL/SQL? 231 §11.2.1 PL/SQL的好处 232 §11.2.1.1 有利于客户/服务器环境应用的运行 232 §11.2.1.2 适合于客户环境 232 §11.2.1.3 客户及服务器端的好处 232 §11.2.2 PL...

    OCPOCA认证考试指南全册:Oracle Database 11g(1Z0-051,1Z0-052,1Z0-053)--详细书签版(第1/2部分)

    8.3.1 存储的与匿名的PL/SQL程序块 282 8.3.2 PL/SQL对象 282 8.4 监视和解决锁定冲突 286 8.4.1 共享锁与排他锁 287 8.4.2 排队机制 287 8.4.3 锁定争用 288 8.4.4 死锁 290 8.5 撤销概述 291 8.6 事务与...

    Oracle8i_9i数据库基础

    §11.2 SQL与PL/SQL 231 §11.2.1 什么是PL/SQL? 231 §11.2.1 PL/SQL的好处 232 §11.2.1.1 有利于客户/服务器环境应用的运行 232 §11.2.1.2 适合于客户环境 232 §11.2.1.3 客户及服务器端的好处 232 §11.2.2 PL...

    Linux环境数据库管理员指南

    3.2.12 PL/SQL 43 3.2.13 模式 43 3.4 安装 43 3.4.1 安装前 44 3.4.2 安装Oracle 8 50 3.4.3 安装Oracle 8i 54 3.4.4 安装后 62 3.5 使用 Oracle 8/8i 65 3.5.1 启动和关闭 66 3.5.2 后台进程 67 3.5.3 创建帐号 ...

    OCA认证考试指南(1Z0-052)

    10.2 识别和管理pl/sql对象 10.3 监视和解析锁定冲突 10.4 小结 10.5 本章测试题 第11章 管理撤销.. 11.1 解释撤销的作用 11.2 了解事务的撤销生成方式 11.3 管理撤销 11.4 小结 11.5 本章测试题...

    PLSQL_Developer7.0中文帮助手册

    4 PL/SQL Developer 7.0 用户指南 6.6 更新数据库............43 6.7 查看和编辑 XMLTYPE 列..44 6.8 直接查询导出..........44 6.9 保存 SQL 脚本..........44 6.10 创建标准查询..........45 7. 命令窗口............

    OCPOCA认证考试指南全册:Oracle Database 11g(1Z0-051,1Z0-052,1Z0-053)--详细书签版(第2/2部分)

    8.3.1 存储的与匿名的PL/SQL程序块 282 8.3.2 PL/SQL对象 282 8.4 监视和解决锁定冲突 286 8.4.1 共享锁与排他锁 287 8.4.2 排队机制 287 8.4.3 锁定争用 288 8.4.4 死锁 290 8.5 撤销概述 291 8.6 事务与...

    ORACLE11G宝典.rar 是光盘里面的内容,书太厚咧没法影印啊

     《Oracle11g宝典》是Oracle数据库管理员、安全管理员、网络管理员、应用开发人员的参考指南,还是Oracle技术支持和培训机构、Oracle学习班、高等院校计算机专业数据库课程的参考教材和上机指导教材。每类人员都...

Global site tag (gtag.js) - Google Analytics