oracle的触发异常方式和把握:
1 自动触发exception: 这种是被动的。
设计expcetion,主要依赖于常见的分类,预期过程会报哪些错误,在异常处理部分就设计相应的报错返回信息,或者是处理。
2 主动触发:raise、raise_applition_error。
raise触发的是,一个exception变量。如果要区分许多类的exception,就要定义许多不同名字的exception变量。这样不太合理。
使用方面,从简约的角度考虑,定义几个exception变量,结合相应的稽核过程或者是稽核函数,根据返回的结果,raise相应对应的变量。
这种应用,子过程处理问题的粒度相对较大,才会有意义。或者是一个exception代表的粒度较大,在处理部分,根据断点变量,用case或if语句处理相关数据。
raise_applition_error的使用比较即兴,ora的值只要小于等于-20001就可以。后面定一个报错的信息。传入exception处理部分进行处理。
3 exception变量的捆绑,#prama exception_init,预期的被动,这个是将常见的分类中,将ora错误定义成一个exception变量。或者是为了处理特定的业务,或者程序bug、异常,定义一个ora的exception变量。
当在处理业务的时候,被动触发了异常,在异常处理部分设计相应的报错返回信息,或者是处理数据。
但是这个exception是过程变量,所以要封装在一个较小的过程里。或者是一个层次里的模块规模(代码量)不应该太大。
4 主动触发方式:
exception 定义变量,用raise来主动触发。这个用法主要是根据业务稽核,判断业务不正确的时候的,raise exception_name。
#pragma exception_init(ora_num, exception_name) 定义一个可预期的变量。如果跟raise_application_error结合使用,那么pragma的定义预期这个值。
#pragma exception_init(ora_num, exception_name) 定义一个可预期的变量,使用被动触发,不调用raise的情况是,是一个预期的特殊处理。比如ora-04068(非普通)。
被动触发:
普通的处理,就是常见的ora异常。DML操作的异常。
例子:
declare
a_b number;
serv_a number;
begin
a_b := 1;
if a_b = 1 then
dbms_output.put_line('1: ab = ' || a_b || ', 第一层');
end if;
begin
a_b := 2;
if a_b = 2 then
dbms_output.put_line('2: ab = ' || a_b || ', 第二层');
end if;
select serv_id into serv_a from ls65_sid.serv_t a where rownum < 3;
dbms_output.put_line('2: 第二层没有报错');
exception
when others then
dbms_output.put_line('2: 第二层报错了'||sqlerrm);
end;
case a_b
when 1 then
dbms_output.put_line('1: 处理完第二层的程序,变量值还留在第一层, a_b = 1, 异常返回点在下一句,不在exception');
when 2 then
dbms_output.put_line('1: 处理完第二层的程序,变量值还留在第二层, a_b = 2, 异常返回点在下一句,不在exception');
else
dbms_output.put_line('1: 例外了');
end case;
dbms_output.put_line('1: 第二层的异常返回后, 接下来是第一层的下一条语句, 并没有传入exception');
exception
when others then
dbms_output.put_line('sqlerrm');
end;
1: ab = 1, 第一层
2: ab = 2, 第二层
2: 第二层报错了ORA-01422: exact fetch returns more than requested number of rows
1: 处理完第二层的程序,变量值还留在第二层, a_b = 2, 异常返回点在下一句,不在exception
1: 第二层的异常返回后, 接下来是第一层的下一条语句, 并没有传入exception
这里检测的是异常的传播。进入了下一句,而非上一层的exception。
另外局部变量a_b的值,不像c++随着块的消失,值会改变。
这是因为oracle是c做的,c是汇编级语言做的,两者的层次不一样,设计的预期功能也是不一样的。
c是遇到块符号{会自动为这个变量开一个空间,遇到}会自动释放。但oracle就不是这么预期了。
raise_application_error注意:
1 raise_application_error(-20001, 'no') 问题。
begin
a_b := 2;
if a_b = 2 then
dbms_output.put_line('2: ab = ' || a_b || ', 第二层');
end if;
raise_application_error(-20001, 'no')
exception
when 'no' then
dbms_output.put_line('2: 第二层报错了'||sqlerrm);
when others then
dbms_output.put_line('2: 第二层报错了'||sqlerrm);
end;
这里的'no' 是错误的。exception的when检测是对exception变量来匹配名字的。
2 no exception 问题
declare
a_b number;
serv_a number;
no exception;
begin
a_b := 2;
if a_b = 2 then
dbms_output.put_line('2: ab = ' || a_b || ', 第二层');
end if;
raise_application_error(-20001, no);
exception
when no then
dbms_output.put_line('2: 第二层报错了'||sqlerrm);
when others then
dbms_output.put_line('2: 第二层报错了'||sqlerrm);
end;
这里有exception变量名也是不对的。因为raise_application_error本身就是异常。
3 应用:
由于raise_application_error比较灵活。它直接就是异常的输出部分。它本身有两个参数,一个是异常的ora编码,一个是编码的说明信息。
所以它在一个过程中应用时,总是会被异常抛出来。
使用方面特别适合触发器。
代码同一层:
触发器:定义DML操作时,当稽核业务不合理,或者相关的DML操作不正确时,直接中断操作。抛出异常,
触发器将异常的ora编码传递给上一层的过程或者函数,进行exception操作。记录报错信息,或者是进行数据处理。
普通应用:调用exception,设置预期的ora编码,在exception进行相关处理。记录报错信息,或者是处理数据。(见下面例子)。
代码不在同一层:
在上一层的代码中设置一个局部变量,将该变量通过in out传入该子过程或者函数、触发器,当在操作时触发了异常exception,就进行赋值。
当out返回变量时,上一层的代码,就根据变量值,进行处理。注意:这里的代码处理方式,不是在exception,而是在调用子过程的下一条语句。(不写例子)
declare
a_b number;
serv_a number;
no exception;
pragma exception_init(no, -20001);
begin
a_b := 1;
if a_b = 1 then
dbms_output.put_line('1: ab = ' || a_b || ', 第一层');
end if;
begin
a_b := 2;
if a_b = 2 then
dbms_output.put_line('2: ab = ' || a_b || ', 第二层');
end if;
raise_application_error(-20001, '主动raise_application_error报错');
exception
when no then
dbms_output.put_line('2: raise_application_error通过exception变量no,进行主动报错');
when others then
dbms_output.put_line('2: 第二层报错了'||sqlerrm);
end;
case a_b
when 1 then
dbms_output.put_line('1: 处理完第二层的程序,变量值还留在第一层, a_b = 1, 异常返回点在下一句,不在exception');
when 2 then
dbms_output.put_line('1: 处理完第二层的程序,变量值还留在第二层, a_b = 2, 异常返回点在下一句,不在exception');
else
dbms_output.put_line('1: 例外了');
end case;
dbms_output.put_line('1: 第二层的异常返回后, 接下来是第一层的下一条语句, 并没有传入exception');
exception
when no then
dbms_output.put_line('1: 主动报错');
when others then
dbms_output.put_line(sqlerrm);
end;
1: ab = 1, 第一层
2: ab = 2, 第二层
2: raise_application_error通过exception变量no,进行主动报错
1: 处理完第二层的程序,变量值还留在第二层, a_b = 2, 异常返回点在下一句,不在exception
1: 第二层的异常返回后, 接下来是第一层的下一条语句, 并没有传入exception
通过以上的练习,对exception就有了灵活的把握和应用了。好的代码,拥有好的结构和逻辑。
这个目标跟两个因素有关系: 1 业务 2 对exception设计思想的把握。
其中,业务比第二点还要重要。它是一个公司吃饭的本事,代码再好总得吃饭。
但是第二点练习好了,自己的能力上去了,到处吃饭都无所谓了。
现实是残忍的,公司的目标和个人的理想是不一致的。
在公司,它需要精益求精的技能,但成本上始终是个工具。员工想加薪水的理想,有点悲剧,所以受打击。
在个人,他需要精益求精的技能,为了能多点薪水,现实不能满足,也没办法总得提高竞争力。