当异常发生时,在块的内部没有该异常处理器时,控制将转到或传播到上一层块的异常处理部分。
BEGIN
DECLARE ---内部块开始
bad_credit;
BEGIN
RAISE bad_credit;
--发生异常,控制转向;
EXCEPTION
WHEN ZERO_DIVIDE THEN --不能处理bad_credite异常
dbms_output.put_line(divide by zero error);
END --结束内部块
--控制不能到达这里,因为异常没有解决;
--异常部分
EXCEPTION
WHEN OTHERS THEN
--由于bad_credit没有解决,控制将转到这里
END;
异常传播
没有处理的异常将沿检测异常调用程序传播到外面,当异常被处理并解决或到达程序最外层传播停止。
在声明部分抛出的异常将控制转到上一层的异常部分。
BEGIN
executable statements
BEGIN
today DATE:=SYADATE; --ERRROR
BEGIN --内部块开始
dbms_output.put_line(this line will not execute);
EXCEPTION
WHEN OTHERS THEN
--异常不会在这里处理
END;--内部块结束
EXCEPTION
WHEN OTHERS THEN
处理异常
END
执行部分抛出的异常将首先传递到同一块的异常部分,如果在同一块的异常部分没有处理这个异常的处理器,那么异常将会传播到上一层的异常部分中,一直到最外层。
在异常部分抛出的异常将控制转到上一层的异常部分。
处理异常将停止异常的传播和解决。有时用户希望在错误发生时,程序仍然能执行一些动作,要达到这个目的,可以把希望执行的动作放在异常处理器中,然后执行不带参数的RAISE语句,RAISE语句将重新抛出出现的异常,允许他继续传播。
DECLARE
order_too_old EXCEPTION;
BEGIN
RAISE order_too_old;
EXCEPTION
WHEN order_too_old THEN
DECLARE
file_handle UTL_FILE.FILE_TYPE;
BEGIN
--open file
file_handle:=UTL_FILE.FOPEN
(location=>/ora01/app/oracle/admin/test/utlsir
,filename=>error.log
.open_mode=>W);
--write error stack
UTL_FILE.PUT_LINE(filehandle,
DBMS_UTILITY.FORMAT_ERROR_STACK);
--write the call stack
UTL_FILE.PUT_LINE(filehandle,
DBMS_UTILITY.FORMAT_CALL_STACK);
--close error log
UTL_FILE.FCLOSE(file_handle);
RAISE; --re-raise the exception
END
END
如果从FORMAT_XXX_STACK输出一个很大的值,那么使用DBMS_OUTPUT或UTL_FILE显示错误或调用堆的异常部分自身也会抛出异常,这两个堆常规下最多能返回2000字节,但utl_file.put_line被限制在1000字节以内,而dbms_output.put_line限制在512字节内。如果使用前面的代码并且不允许这种可能性,那么在异常处理器中将抛出一个未处理的异常。
GOTO语句不能用于将控制从执行部分传递到异常部分或反之。
已命名异常
在PL/SQL块的异常部分只有已命名的异常才能被WHEN子串处理,ORACLE包含了一系列已命名的异常,这些异常都声明在STANDARD包中,这些内建异常在这里就不一一讲述,有兴趣的读者可以查阅有关资料。
常用异常处理方法
除了标准异常外,用户可以声明自己的已命名异常。比如商业规则错误,或者将自定义异常与一个javascript:window.open("http://pagead2.googlesyndication.com/pagead/iclk?sa=l&ai=BsTnkbTLFRqHaB5aygwPDpJ20A-zBhiesleSUA8CNtwHwkwkQBRgFIJmu8QkoFDABOAFQwu2bvvr_____AWCdudCBkAWYAYeHAZgBgZMGoAHo9bP3A6oBCjIwMDAwMTIxNTSyAQ50ZWNoLmRkdmlwLmNvbcgBAdoBM2h0dHA6Ly90ZWNoLmRkdmlwLmNvbS8yMDA2LTA0LzExNDQyMDczMjAzODE0XzMuaHRtbIACAakCUaKVEYingT7IAqin1gKoAwE&num=5&adurl=http://www.8844.io&client=ca-pub-5384462698219144");GgKwClickStat("数据库","www.8844.io","afc"); onmouseout=isShowGg = false;InTextAds_GgLayer="_u6570_u636E_u5E93">数据库号相联系,甚至可以将javascript:window.open("http://pagead2.googlesyndication.com/pagead/iclk?sa=l&ai=BsTnkbTLFRqHaB5aygwPDpJ20A-zBhiesleSUA8CNtwHwkwkQBRgFIJmu8QkoFDABOAFQwu2bvvr_____AWCdudCBkAWYAYeHAZgBgZMGoAHo9bP3A6oBCjIwMDAwMTIxNTSyAQ50ZWNoLmRkdmlwLmNvbcgBAdoBM2h0dHA6Ly90ZWNoLmRkdmlwLmNvbS8yMDA2LTA0LzExNDQyMDczMjAzODE0XzMuaHRtbIACAakCUaKVEYingT7IAqin1gKoAwE&num=5&adurl=http://www.8844.io&client=ca-pub-5384462698219144");GgKwClickStat("数据","www.8844.io","afc"); onmouseout=isShowGg = false;InTextAds_GgLayer="_u6570_u636E">数据库错误号赋予自定义异常。PL/SQL的这个性能极大的提高了对异常的javascript:window.open("http://pagead2.googlesyndication.com/pagead/iclk?sa=l&ai=Bd7spbTLFRqHaB5aygwPDpJ20A4WVgS-p8rzVA8CNtwGA0w4QChgKIJmu8QkoFDgBULT4vr4HYJ250IGQBZgBgZMGqgEKMjAwMDAxMjE1NLIBDnRlY2guZGR2aXAuY29tyAEB2gEzaHR0cDovL3RlY2guZGR2aXAuY29tLzIwMDYtMDQvMTE0NDIwNzMyMDM4MTRfMy5odG1sqQJRopURiKeBPqgDAQ&num=10&adurl=http://www.zzyedu.com/jpkc/qiguan.htm&client=ca-pub-5384462698219144");GgKwClickStat("管理","www.zzyedu.com","afc"); onmouseout=isShowGg = false;InTextAds_GgLayer="_u7BA1_u7406">管理和处理的能力。
声明自己的异常
如果你希望声明自己的异常,那么你可以在异常处理器中包括WHEN子句,声明的异常的作用范围与声明的变量相同。声明在外层块的异常能被该块和它的子块访问,但声明在子块的异常不能被父块处理。
例:
BEGIN
DECLARE
Insufficient_credite EXCEPTION;
BEGIN
RASISE Insufficient_credite;
EXCEPTION
WHEN Insufficient_credite THEN
--可以在此处理异常
extend_credite(cust_id);
END -嵌套块结束
EXCEPTION
WHEN Insufficient_credite THEN
--超出范围,不能在这里处理异常
END;
如果声明的异常与内建的异常同名,那么当引用异常将解决你的异常而不是内建的异常。
给javascript:window.open("http://s1.17luntan.com/ClickPortal/WebClick.aspx?id=20656&k=%u6570%u636E%u5E93&siteid=b81b117a-fb9d-4281-b895-957f74acde85&url=http%3A//tech.ddvip.com/2006-04/11442073203814_4.html&gourl=http%3A//go.microsoft.com/%3Flinkid%3D6331217&parm=2AD35D168D9B391205ED2226C98DF5004AD4CEA6676F86A6&alliedsiteid=0"); onmouseout="isShowAds = false;isShowAds2 = false">数据库错误命名
如果希望处理一个异常,那么异常必须有一个名字,javascript:window.open("http://pagead2.googlesyndication.com/pagead/iclk?sa=l&ai=BmQILgTLFRvneM5qQgQOW4uikB-zBhiesleSUA8CNtwHwkwkQBhgGIJmu8QkoFDABOAFQwu2bvvr_____AWCdudCBkAWYAYWTBqAB6PWz9wOqAQoyMDAwMDEyMTU0sgEOdGVjaC5kZHZpcC5jb23IAQHaATNodHRwOi8vdGVjaC5kZHZpcC5jb20vMjAwNi0wNC8xMTQ0MjA3MzIwMzgxNF80Lmh0bWyAAgGpAlGilRGIp4E-yAKop9YCqAMB&num=6&adurl=http://www.8844.io&client=ca-pub-5384462698219144");GgKwClickStat("数据","www.8844.io","afc"); onmouseout=isShowGg = false;InTextAds_GgLayer="_u6570_u636E">数据库错误有数千个,但是只有不到25个是内建的已命名异常,但需要处理这些未命名的异常时,你可以将一个名字和一个错误号联系在一起,达到这个目的的语句是:PRAGMA EXCEPTION_INIT语句
语法如下:
PRAGMA EXCEPTION_INIT(exception_name,error_number);
执行这个语句之前,必须首先声明异常名。
DECLARE
invald_table_name EXCEPTION;
PRAGMA EXCEPTION_INIT(invald_table_name,-942);
BEGIN
EXCEPTION
WHEN invald_table_name THEN
UTL_FILE.PUT_LINE(file_handle,user || UID || hit a bad table);
END;
另外一种处理数据库错误的方法是使用内建函数SQLCODE和SQLERRM,这两个函数在包一级声明,SQLCODE将返回现行数据库错误号,这些错误号中除了NO_DATA_FOUND是+100外其他都是负数。SQLERRM返回文本描述的错误信息。为了获得用户自定义异常返回的SQLERRM和SQLCODE,你需要使用RAISE_APPLICATION_ERROR函数给自定义异常标注错误号。
给自定义错误标注号码
RAISE_APPLICATION_ERROR内建函数用于抛出一个异常并给异常赋予一个错误号以及错误信息。自定义异常的缺省错误号是+1,缺省信息是User_Defined_Exception。来自未处理的异常的一般信息对于识别导致错误的原因没有帮助,RAISE_APPLICATION_ERROR函数能够在pl/sql程序块的执行部分和异常部分调用,显式抛出带特殊错误号的命名异常。
RAISE_APPLICATION_ERROR(error_name,error_message[,{TRUE|| FALSE}]);
错误号的范围是-20,999到-20,999。错误信息是文本字符串,最多为2048字节。TRUE和FALSE表示是添加(TRUE)进错误堆(ERROR STACK)还是覆盖(overwrite)错误堆(FALSE)。缺省情况下是FALSE。
IF product_not_found THEN
RAISE_APPLICATION_ERROR(-20123,Invald product code TRUE);
END IF;