一直以来有一个困惑,一直没解决,昨天一哥们问我这个问题,决心弄清楚,终于获得了答案。
先看下面这个函数:java
create or replace function fn_test(c_xm varchar) return varchar2 as V_P1 varchar(10); begin select name into V_p1 from t1 where 1 = 2;--将name查出赋值给v_p1 return 'test' || c_xm; end;
这个函数很简单,是我写的一个测试函数,没什么意义,“select name into V_p1 from t1 where 1 = 2;”这句话有经验的人一看就知道它会报错,由于这个查询返回的结果集是空,会报一个错,将其赋值时,pl/sql引擎会认为它没有数据,是一个null,这很相似于java中的空指针异常。当咱们调试该函数的时候,到这一句,马上会报ORA-1403错误:没有数据。
可是若是在sql中调用该函数呢?执行如下查询:
select fn_test('1') from dual;
结果是返回一个空记录,没有任何报错。这是为何呢?难道遇到了bug?若是是存储过程呢?不管如何调试仍是直接调用,此处都会报错,有兴趣的能够验证一下,我就不验证了,由于我以前碰到过许屡次了,因此通常在select into时,若是没有把握这个结果集必定有,都会select count一下而后再into。
这到底是怎么回事呢?
下面是个人猜想:
对于查不到结果集来讲,这不是什么很严重的错误,没有就没有了,不用报错吧?如咱们执行一条sql,select * from t1 where 1=2;若是这个sql没有查到数据,难道就非得报个错?基于这个考虑,在sql调用函数时,若是这种NO__DATA_FOUND的异常,可能sql解析器就直接处理了,不用再报错了.由于它并不像诸如找不到表、找不到字段、没有权限等等的错误严重。
这仅仅是猜想,可是究竟为何呢?
最后我在asktom的网站上找到了答案:sql
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::p11_question_id:10321465390114
从1楼这哥们的提问来看,他也是很郁闷,他甚至和我提出了同样的疑问:“为何过程就没有这种问题?”
我看完了后,总结了一下,缘由大体为:
当select a into b 时,若是没有命中结果集,则当前的查询为空,在sql语句的查询中,解析器仅仅是认为“没有数据(no data found)”而已,而不是将它做为一个错误,而后返回一个null,函数就此中止再也不往下执行,tom的解释也比较诙谐:oracle
Under the covers, SQL is raising back to the client application "hey buddy -- no_data_found". The client in this case says "ah hah, no data found means 'end of data'" and stops.
可是在pl/sql中却不是,pl/sql的处理方式倒是将它认为是一个错误,app
Under the covers, PLSQL is raising back to the client application "hey -- no_data_found. The client in this case says "uh-oh, wasn't expecting that from PLSQL -- sql sure, but not PLSQL. Lets print out the text that goes with this exceptional condition and continue on"
NO_DATA_FOUND并非一个错误,并且一个意外的状况,这相似于空指针异常,而这个意外状况只是没找到数据而已,当调用者不一样时,对其的处理也不一样,当sql查询调用时,遇到这个异常就认为是没有数据,而后返回一个Null,可是当PL/sql调用时,会认为这是一个很差的状况,转由异常处理块处理。
归根结底一句话,NO_DATA_FOUND都会由调用者捕获,只是调用者对这个异常的处理方式不同而已。若是想在sql调用时报错怎么办?其实很简单,捕获这个NO_DATA_FOUND异常,而后raise便可:函数
create or replace function fn_test(c_xm varchar) return varchar2 as V_P1 varchar(10); begin select name into V_p1 from t1 where 1 = 2; return 'test' || c_xm; exception when no_data_found then /*RAISE_APPLICATION_ERROR(-20000, 'no data found');*/--抛出自定义的异常也行 raise program_error; end;
这样,当再执行到该句时,马上转到异常处理块,抛出一个非NO_DATA_FOUND异常,调用者不认识,认为是一个错误或者很严重的异常,只能报错给客户端了。oop
begin: select fn_test('1') from dual;--开始解析sql查询语句 call fn_test;--发现值来自于函数,开始调用fn_test var result;--定义临时变量接收结果 try{ result=fn_test('1'); }catch(NO_DATA_FOUND){--若是是NO_DATA_FOUND异常则null处理 result=null; }catch(OTHERS){--若是其余异常则抛出 throw others; } select result from dual; end;
以上过程模拟了select语句调用函数的过程,若是出现了异常,在报异常的地方函数就此中止运行,再也不往下执行。
验证:post
create or replace function fn_test(c_xm varchar) return varchar2 as V_P1 varchar(10); begin select name into V_p1 from t1 where 1 = 2;--NO_DATA_FOUND,也会报错,可是sql解析器会以null返回 select 1/0 into v_p1 from dual;--除数为0,会报错 return 'test' || c_xm; end;
当再次执行selectu语句的时候,并无报除数为0的错误,由于查询在第一条语句就中止了,再也不往下执行,若是去掉第一条语句:测试
create or replace function fn_test(c_xm varchar) return varchar2 as V_P1 varchar(10); begin --select name into V_p1 from t1 where 1 = 2;--NO_DATA_FOUND,注释掉该行 select 1/0 into v_p1 from dual;--除数为0,会报错 return 'test' || c_xm; end;
执行查询,马上报错:ORA-01476:除数为0。以下图:网站
当执行了异常处理时,若发生了异常,则会当即跳转到异常块中,这和java是同样的,能够选择捕获NO_DATA_FOUND异常而后外抛。this
create or replace function fn_test(c_xm varchar) return varchar2 as V_P1 varchar(10); begin select name into V_p1 from t1 where 1 = 2;--NO_DATA_FOUND,会当即跳转到exception块,再也不继续执行 select 1 / 0 into v_p1 from dual; --除数为0,会报错,可是这句没有机会执行了 return 'test' || c_xm; exception when NO_DATA_FOUND then raise_application_error('-20000', '没找到数据');--异常外抛给调用者,直接报错 end;
以下图:
也能够在异常中返回一个有意义的提示,告诉调用者一个有意义的信息,如:
create or replace function fn_test(c_xm varchar) return varchar2 as V_P1 varchar(10); begin select name into V_p1 from t1 where 1 = 2;--NO_DATA_FOUND,会当即跳转到exception块,再也不继续执行 select 1 / 0 into v_p1 from dual; --除数为0,会报错,可是这句没有机会执行了 return 'test' || c_xm; exception when NO_DATA_FOUND then return '没有找到数据!'; end;
结果以下图:
这个结论适用于其余状况,不管是在loop中,仍是单一查询,只要报了NO_DATA_FOUND异常,都会当即stop,要么跳转到exception,要么返回null,再也不继续执行,其实原理很简单,和java是同样的,很好理解。