【转】 oracle exception总结(读书笔记)


【转】 oracle exception总结(读书笔记)
2011年11月04日
  一,基础-什么是异常情态?
  1):exception即异常情态,如:
  DECLARE
  exptname EXCEPTION;       --异常情态的定义
  [PRAGMA EXCEPTION_INIT;]  --异常情态与run time error number相关联
  .
  .
  BEGIN
  .
  .
  END;
  每一个异常情态都【应当】有一个exception handler(异常处理器)与之对应,
  从而在【代码执行部分】发生对应的异常情态(exception也可叫run time error)
  时处理异常,而防止异常扩大。
  当然你也可以只声明异常情态,不与相应的run time error number对应,
  甚至没有对应的exception handler,这时的这个异常情态声明是没有意义的:
  DECLARE
  exptname EXCEPTION;
  BEGIN
  DBMS_OUTPUT.PUT_LINE('Hello World!');
  END;
  2):自定义与预定义异常
  DECLARE
  exptname EXCEPTION;
  BEGIN
  DBMS_OUTPUT.PUT_LINE('Hello World!');
  RAISE exptname;
  END;
  上面只定义了异常情态,而没有对应的exception handler,所以RAISE时
  会出现run time error,因为RAISE也是在BEGIN .. END之间的,所以也是
  一个run time的实际执行部分。
  预定义异常:
  BEGIN
  RAISE CASE_NOT_FOUND;
  EXCEPTION WHEN CASE_NOT_FOUND then
  DBMS_OUTPUT.PUT_LINE('case语句没有正确结束!');
  END;
  这些预定义的异常都是在standard包(这个包中有大部分系统函数,如:left,right等)中预先定义的,你也可以在包中定义自己的
  异常:
  CREATE OR REPLACE PACKAGE test_pkg AS
  exptname EXCEPTION;
  END;
  BEGIN
  RAISE test_pkg.exptname;
  EXCEPTION WHEN test_pkg.exptname THEN
  DBMS_OUTPUT.PUT_LINE('触发了自定义异常!');
  END;
  3):异常情态的流程
  注意:异常情态是在定义部分定义的(预定义异常除外),执行部分(begin .. end中)触发的,exception handler
  中处理的。
  看完上面的流程你应该有一个疑问:定义部分和handler部分的异常又怎样捕获呢?
  答案当然是在其包含块中捕获,看下面:
  BEGIN
  DECLARE
  v_int INT:='ABC';
  BEGIN
  NULL;
  EXCEPTION WHEN OTHERS THEN
  DBMS_OUTPUT.PUT_LINE('I can only check then BEGIN .. END part error!'||chr(10)||'Other part i can''t check!');
  END;
  EXCEPTION WHEN OTHERS THEN
  DBMS_OUTPUT.PUT_LINE('BEGIN .. END part have error occured!'||chr(10)||'Please check that part!');
  END;  
  上面的父块就成功捕获了调用块中的定义部分的run time error,而被调用块的异常部分没有被执行;
  其实很好理解,对于子块如果定义部分出现了错误,它的BEGIN .. END部分代码都不会被执行,
  而handler只处理BEGIN .. END部分出现的异常情态,所以当然就捕获不到定义部分的异常。
  对于异常handler部分出现的异常更好理解了,因为handler只要处理BEGIN .. END中的异常情态:
  BEGIN
  DECLARE
  CURSOR test_cur IS SELECT 1 FROM DUAL;
  BEGIN
  RAISE CURSOR_ALREADY_OPEN;
  EXCEPTION WHEN CURSOR_ALREADY_OPEN THEN
  CLOSE test_cur;
  WHEN INVALID_CURSOR THEN
  DBMS_OUTPUT.PUT_LINE('游标已经关闭或者没有这个游标的声明!');
  END;  
  EXCEPTION WHEN INVALID_CURSOR THEN
  DBMS_OUTPUT.PUT_LINE('游标已经关闭或者没有这个游标的声明!');
  END;
  4):异常作用域
  BEGIN
  DECLARE
  exptname EXCEPTION;
  BEGIN
  RAISE exptname;
  END;
  EXCEPTION WHEN exptname THEN
  DBMS_OUTPUT.PUT_LINE('触发了子块中异常!');
  END;
  上面的语句是不能执行的,因为exptname的作用域只在子块中生效,
  其实异常情态定义和变量定义一样,作用域生效原则和变量也是一样的。
  鉴于上面的原因,如果有必要可以预定义自己的异常:
  CREATE OR REPLACE PACKAGE test_pkg AS
  exptname EXCEPTION;
  END;
  BEGIN
  BEGIN
  RAISE test_pkg.exptname;
  END;
  EXCEPTION WHEN test_pkg.exptname THEN
  DBMS_OUTPUT.PUT_LINE('触发了子块中异常!');
  END;
  5):常见的预定义情态
  INVALID_CURSOR  --发生关闭已经关闭的游标或者不存在的游标时出现
  CURSOR_ALREADY_OPEN  
  NO_DATA_FOUND  --针对select .. into ..,没有返回行时,或者引用未初始化的plsql索引表
  TOO_MANY_ROWS  --select .. into 返回多行
  INVALID_NUMBER与VALID_ERROR  --字符串不能转换成数字错误
  过程性语句中字符串不能转换成数字,会引发VALUE_ERROR异常情态:
  DECLARE
  v_int INT;
  v_varchar2 varchar2(100);
  BEGIN
  v_varchar2:='a12';
  v_int:=v_varchar2;
  EXCEPTION WHEN VALUE_ERROR THEN
  v_int:=REGEXP_REPLACE(v_varchar2,'[^[:digit:]]','' );
  DBMS_OUTPUT.PUT_LINE(v_int);
  END;
  sql语句中则会引发INVALID_NUMBER异常情态:
  CREATE TABLE test(x INT);
  BEGIN
  INSERT INTO TEST VALUES('AB123');
  EXCEPTION WHEN INVALID_NUMBER THEN
  DBMS_OUTPUT.PUT_LINE('无效数字!');
  END;
  还有两个特殊的异常:
  PGROGRAM_ERROR和STORAGE_ERROR
  如果出现STORAGE_ERROR则证明内在不够,如果出现PROGRAM_ERROR则说明你要联系oracle
  了,因为program_error是PL/SQL引擎出现错误。
  6):异常handler关键字or,多异常情况匹配
  BEGIN
  RAISE VALUE_ERROR;
  RAISE INVALID_NUMBER;  --注意:这行不执行,因为只要异常情态触发了,就不再返回BEGIN .. END部分执行下面的代码了
  EXCEPTION WHEN INVALID_NUMBER OR VALUE_ERROR THEN
  DBMS_OUTPUT.PUT_LINE('Not numeric!');
  END;
  7):sqlcode与sqlerrm【过程性函数】
  DECLARE
  x VARCHAR(200);
  y VARCHAR2(200);
  BEGIN
  RAISE NO_DATA_FOUND;
  EXCEPTION WHEN OTHERS THEN
  x:=SQLCODE;
  y:=SQLERRM(SQLCODE);
  INSERT INTO test VALUES(x,y);
  insert into test VALUES(SYSDATE,SYSTIMESTAMP);
  END;
  注意,因为sqlcode与sqlerrm是过程性的函数,所以不可以在sql语句下直接使用,不能像sysdate等系统函数。
  DECLARE
  x VARCHAR2(100);
  y VARCHAR2(200);
  BEGIN
  x:=SQLCODE;
  y:=SQLERRM(0);
  --SELECT SQLERRM(0) INTO y FROM DUAL;  不能在sql中使用过程性函数,可能跟纯度级别有关系吧
  DBMS_OUTPUT.PUT_LINE(x||chr(9)||y);
  END;
  且sqlerrm只会返回系统内部的oracle错误信息,不会返回用户自定义的信息:
  BEGIN
  RAISE NO_DATA_FOUND;
  EXCEPTION WHEN NO_DATA_FOUND THEN
  DBMS_OUTPUT.PUT_LINE('Hello '||SQLERRM);
  END;  
  如果返回用户定义的消息,岂不死循环了。
  二,异常传播机制
  其实在上面的叙述中都已经一一详细介绍过。
  三,plsql常用异常处理模块--实际应用部分
  1):dbms_utility.format_call_stack与dbms_utility.format_error_stack简单使用
  create or replace procedure error_test1 as
  begin
  DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_CALL_STAC K);
  raise value_error;
  end;
  create or replace procedure error_test2 as
  begin
  error_test1;
  exception when others then
  DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_STA CK);
  end;
  注意:想得到正确的调用堆栈要把dbms_utility.format_call_stack放在最终调用子程序中,
  因为调用方向总是从外向里调用,所以要得到详细的堆栈当然要沿着调用路线。
  错误堆栈则相反,因为异常总是按从里向外路线传递,所以想捕获整个错误堆栈就要在祖块捕获。
  2):在《Oracle 8i PL/SQL程序设计》一书的P265有一个详细的利用dbms_utility.format_error_stack和dbms_utility.format_call_stack的
  例子程序,非常不错,可以详细的看一看。

猜你喜欢

转载自rhj485hm.iteye.com/blog/1359473
今日推荐