plsql的错误信息与异常处理

7 错误消息与异常处理

7.1 异常的介绍

(1) 处理异常分为三个步骤:
  A 声明异常
  B 引发异常
  C 处理异常

(2) 异常的特征
  A 错误类型: ORA-xxxxx  运行时错误
               PLS-xxxxx  编译错误
  B 错误代码:xxxxx
  C 错误的文本描述

案例1:编译错误的案例PLS

SQL> create or replace procedure p1 is
  2  begin
  3  null;
  4  end;
  5  /

Procedure created.

SQL> create or replace procedure p1 is
  2  begin
  3  null           --特意不写分号
  4  end;
  5  /

Warning: Procedure created with compilation errors.         --出现警告

SQL> show error     --查看错误消息
Errors for PROCEDURE P1:

LINE/COL ERROR
-------- -----------------------------------------------------------------
4/1     PLS-00103: Encountered the symbol "END" when expecting one of the
     following:
     ;
     The symbol ";" was substituted for "END" to continue.

案例2:运行时错误 -- 在编译的时候没有错误,但是在执行的时候发生错误。

SQL> create or replace procedure p2 is
  2   v_descr varchar2(20);
  3  begin
  4   select hrc_descr
  5     into v_descr
  6    from hrc_tab
  7   where hrc_code=8;
  8  dbms_output.put_line(to_char(v_descr));
  9  end;
 10  /

Procedure created.

SQL> exec p2;       --运行一个存储过程
BEGIN p2; END;

*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "PLSQL.P2", line 4
ORA-06512: at line 1

总结:
 A PLSQL错误 -- 编译错误,在执行之前就已经报错,需要检查程序,修改程序,debug
 B ORA错误   -- 运行时错误,出现这种错误的时候需要手工处理,可以采用第三方软件的单步调试方式处理

(2) 异常处理中的声明,分为三个部分

 A exception声明,在声明自己定义异常的时候需要用到这个方法。
 B raise语句:显示地引发异常
 C pragma excetption_init 这个指令可以将oracle错误和自己定义异常关联起来

 函数(需要定义变量来接收)
     
    sqlcode -- 返回错误的代码号,如果没有错误就返回0,可以根据sqlcode返回的值查询官方文档,获得更详细的错误描述
    sqlerrm -- 返回错误的文本描述,如果没有错误返回normal或者successful completion,也就是官方文档定义的错误

(3) 异常处理的常见案例
declare
  v_descr varchar2(20);
begin
  select hrc_descr
    into v_descr
    from hrc_tab
   where hrc_code=8;
  dbms_output.put_line(v_descr);
exception when no_data_found then       --异常的名称
  dbms_output.put_line('not exists');
end;

no_data_found  --oracle预定义好的异常的名称,oracle官方文档上有每个异常名称描述和引发的场景介绍
《PL/SQL User's Guide and Reference》pdf版本中第264页

sqlcode与sqlerrm两个函数的使用

declare
  v_descr varchar2(20);
  v_sqlcode number;
  v_sqlerrm varchar2(200);
begin
  select hrc_descr
    into v_descr
    from hrc_tab
   where hrc_code=8;
  dbms_output.put_line(v_descr);
exception when no_data_found then
  v_sqlcode:=sqlcode;
  v_sqlerrm:=sqlerrm;
  dbms_output.put_line('not exists');
  dbms_output.put_line('ERR: an error with info :'||to_char(v_sqlcode));
  dbms_output.put_line(v_sqlerrm);
end;

输出:
not exists
ERR: an error with info :100        --100是错误的代码号,其他的错误sqlcode都是ora-后面的号码,这个异常特殊
ORA-01403: no data found            --错误的描述

对程序的异常进行处理,让程序不会在发生异常

declare
  v_descr varchar2(20);
  v_sqlcode number;
  v_sqlerrm varchar2(200);
begin
  select hrc_descr
    into v_descr
asdfasdg    from hrc_tab
   where hrc_code=8;
  dbms_output.put_line(v_descr);
exception when no_data_found then
  v_sqlcode:=sqlcode;
  v_sqlerrm:=sqlerrm;
  dbms_output.put_line('not exists');
  dbms_output.put_line('ERR: an error with info :'||to_char(v_sqlcode));
  dbms_output.put_line(v_sqlerrm);
  insert into hrc_tab values(8,'asdfasdg');
  commit;
end;

第一次运行

输出:
not exists
ERR: an error with info :100       
ORA-01403: no data found

再一次运行
输出:
asdfasdg

(4)PLSQL异常的功能性分类

  A 预定义的异常  oracle自己预先定义好的
  B 用户自定义的异常

 用户自定义的异常

declare
  site_s_undefined_for_org exception;
  v_cnt number;
begin
  select count(*) into v_cnt from org_site_tab where org_id=1007;  --本身查询是没问题的
  if v_cnt=0 then      --只有在v_cnt值为0的时候引发异常
    raise site_s_undefined_for_org;
  end if;
exception when site_s_undefined_for_org then
  dbms_output.put_line('empty table!');
  when others then
  dbms_output.put_line('ERR: an error with info :'||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

--自己定义异常,自己抛出异常,自己处理异常

系统预定义的异常

DUP_VAL_ON_INDEX   --唯一性约束上有列值的重复冲突

declare
  site_s_undefined_for_org exception;
  v_cnt number;
begin
  select count(*) into v_cnt from org_site_tab where org_id=1007;  --本身查询是没问题的
  insert into hrc_tab values(8,'asfdadsagsa');--这里出现异常,程序就进入异常处理部分,后面不再执行
  commit;
  if v_cnt=0 then      --只有在v_cnt值为0的时候引发异常
    raise site_s_undefined_for_org;
  end if;
exception when site_s_undefined_for_org then
  dbms_output.put_line('empty table!');
  when DUP_VAL_ON_INDEX then
  dbms_output.put_line('value repeat!');
  when others then
  dbms_output.put_line('ERR: an error with info :'||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

输出:value repeat!

(3) pragma exception_init 指令
 这个指令就是把oracle的错误还有用户自定义异常关联起来

[oracle@test ~]$ oerr ora 02290     --知道错误号,可以使用该命令查看详细错误
02290, 00000, "check constraint (%s.%s) violated"
// *Cause: The values being inserted do not satisfy the named check
//    constraint.
// *Action: do not insert values that violate the constraint.


select * from user_constraints where table_name='ORG_LEVEL';

SQL> conn plsql/plsql
Connected.
SQL> insert into org_level values(1001,'P');
insert into org_level values(1001,'P')
*
ERROR at line 1:
ORA-02290: check constraint (PLSQL.ORG_LEVEL_CK) violated


declare
  invalid_org_level exception;
  pragma exception_init(invalid_org_level,-2290);  --关联以后,就不需要raise引发异常
begin
create table exception_monitor(
excep_tab_name varchar2(30),
excep_key varchar2(50),
excep_program varchar2(30),
excep_name varchar2(30),
excep_code number,
excep_txt varchar2(200),
excep_date date
);
  insert into org_level values(1001,'P');
  commit;
exception when invalid_org_level then
  dbms_output.put_line('ERR:an error with info :'||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
  when others then
  dbms_output.put_line('ERR:an error with info :'||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

可以让程序自己抛出

begin
  insert into org_level values(1001,'P');
  commit;
exception
  when others then
  dbms_output.put_line('ERR:an error with info :'||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

##########################################################################################

成都创新互联长期为千余家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为阿巴嘎企业提供专业的网站制作、成都网站设计,阿巴嘎网站改版等技术服务。拥有10多年丰富建站经验和众多成功案例,为您定制开发。


7.2 异常监控表

(1) 创建一个异常监控表:exception_monitor

字段
   发生异常的表的名字:    excep_tab_name
   发生异常的行的主键:    excep_key
   发生异常的程序的名称:  excep_program,如果是匿名块就置为null
   异常的名称:            excep_name 如果没有定义就写'others'
   异常的sqlcode:          excep_code
   异常的文本描述:        excep_txt
   发生异常的时间:        excep_date

以后编写程序的时候都要编写异常处理部分,获取到上面这些信息,插入这个表

创建异常监控表:
create table exception_monitor(
excep_tab_name varchar2(30),
excep_key varchar2(50),
excep_program varchar2(30),
excep_name varchar2(30),
excep_code number,
excep_txt varchar2(200),
excep_date date
);

改写上面的例子:
declare
  invalid_org_level exception;
  pragma exception_init(invalid_org_level,-2290);
  v_sqlcode number;
  v_sqlerrm varchar2(200);
begin
  insert into org_level values(1001,'P');
  commit;
exception when invalid_org_level then
  v_sqlcode:=sqlcode;
  v_sqlerrm:=sqlerrm;
  insert into exception_monitor values('ORG_LEVEL','1001',null,upper('invalid_org_level'),v_sqlcode,v_sqlerrm,sysdate);
  commit;
  when others then
  v_sqlcode:=sqlcode;
  v_sqlerrm:=sqlerrm;
  insert into exception_monitor values('ORG_LEVEL','1001',null,upper('others'),v_sqlcode,v_sqlerrm,sysdate);
  commit;
end;

练习7:将练习6那个程序修改它的异常处理部分,将错误捕获到监控表

错误号的20000~21299是错误号的空缺范围,这个范围用来自定义错误,用内置的函数来引发这个错误。

declare
  site_s_undefined_fo_org exception;
  pragma exception_init(site_s_undefined_fo_org,-20001);
  v_cnt number;
begin
  select count(1) into v_cnt from org_site_tab where org_id=1007;
  if v_cnt=0 then
    raise_application_error(-20001,'this table rows is empty!');
  end if;
exception when site_s_undefined_fo_org then
  dbms_output.put_line(sqlerrm);
  when others then
  dbms_output.put_line('ERR : an error with info :'||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

不关联异常的名称也可以:

declare
  v_cnt number;
begin
  select count(1) into v_cnt from org_site_tab where org_id=1007;
  if v_cnt=0 then
    raise_application_error(-20001,'this table rows is empty!');
  end if;
exception
  when others then
  dbms_output.put_line('ERR : an error with info :'||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

##########################################################################################

7.3 在声明部分引发的异常的处理

注意:异常需要在begin 和 exception 之间才能捕获到的

declare
  v_cnt number(2):=100;
begin
  null;
exception when others then
  dbms_output.put_line('ERR CODE : '||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

改写:

begin
  declare
    v_cnt number(2):=100;
  begin
    null;
  exception when others then
    dbms_output.put_line('ERR CODE : '||to_char(sqlcode));
    dbms_output.put_line(sqlerrm);
  end;
exception when others then
  dbms_output.put_line('ERR CODE : '||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

解决方法:将原来的代码块嵌套在一个begin和exception之间即可捕获到

注意:
 A 程序是从begin开始执行的,declare部分不是程序执行的部分
 B 异常捕获的区间是begin到exception之间的代码

7.5 在异常部分引发异常的处理

declare
  condition boolean:=true;
  excep1 exception;
  excep2 exception;
begin
  if condition then
    raise excep1;
  end if;
exception when excep1 then
  raise excep2;
end;

改写:

declare
  condition boolean:=true;
  excep1 exception;
  excep2 exception;
begin
  if condition then
    raise excep1;
  end if;
exception when excep1 then
  begin
    raise excep2;
  exception when excep2 then
    dbms_output.put_line('ERR CODE : '||to_char(sqlcode));
    dbms_output.put_line(sqlerrm);
  end;
end;

或者

declare
  condition boolean:=true;
  excep1 exception;
  excep2 exception;
begin
  begin
    if condition then
      raise excep1;
    end if;
  exception when excep1 then
    raise excep2;
  end;
exception when excep2 then
  dbms_output.put_line('ERR CODE : '||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;

7.6 一个异常可以被多次引发

declare
  condition boolean:=true;
  excep1 exception;
begin
  begin
    if condition then
      raise excep1;
    end if;
  exception when excep1 then
    raise excep1;
  end;
exception when excep1 then
  dbms_output.put_line('ERR CODE : '||to_char(sqlcode));
  dbms_output.put_line(sqlerrm);
end;


文章标题:plsql的错误信息与异常处理
文章位置:http://scyanting.com/article/pppdio.html