PLSQLexception异常处理.docx
- 文档编号:29396469
- 上传时间:2023-07-23
- 格式:DOCX
- 页数:23
- 大小:21.07KB
PLSQLexception异常处理.docx
《PLSQLexception异常处理.docx》由会员分享,可在线阅读,更多相关《PLSQLexception异常处理.docx(23页珍藏版)》请在冰豆网上搜索。
PLSQLexception异常处理
ORACLEPL/SQL异常处理(Exception)学习--比较全,看完了基本应用及原理就了解了收藏
1、PL/SQL错误类型
错误类型
报告者
处理方法
编译时错误
PL/SQL编译器
交互式地处理:
编译器报告错误,你必须更正这些错误
运行时错误
PL/SQL运行时引擎
程序化地处理:
异常由异常处理子程序引发并进行捕获
2、异常的声明
有两种异常:
用户自定义异常和预定义异常
用户自定义异常就是由程序员自己定义的一个错误。
该错误还不是非常重要,所以并没有将整个错误包含在Oracle的错误中。
例如,它可能是一个与数据有关的错误。
而预定义异常则对应于一般的SQL和PL/SQL错误。
用户自定义异常是在PL/SQL块的声明部分声明的。
像变量一样,异常也有一个类型(EXCEPTION)和有效范围。
例如:
viewplaincopytoclipboardprint?
DECLARE
Exception_nameEXCEPTION;
…
DECLARE
Exception_nameEXCEPTION;
…
3、异常的引发
与异常相关联的错误发生的时候,就会引发相应的异常。
用户自定义异常是通过RAISE语句显式引发的,而预定义异常则是在它们关联的ORACLE错误发生的时候隐式引发的。
如果发生了一个还没有和异常进行关联的ORACLE错误的时候,也会引发一个异常。
该异常可以使用OTHERS子程序进行捕获。
预定义的异常也可以使用RAISE进行显式地引发,如果需要这样做的话。
viewplaincopytoclipboardprint?
…
RAISEexception_name;
…
…
RAISEexception_name;
…
4、异常的处理
发生异常的时候,程序的控制就会转移到代码块的异常处理部分。
异常处理部分是由异常处理子程序组成的,这些异常处理子程序可以是针对某些异常的,也可以是针对所有异常的。
与该异常相关联的错误发生,并引发了该异常的时候,就会执行异常处理部分的代码。
异常处理部分的语法如下:
viewplaincopytoclipboardprint?
EXCEPTION
WHENexception_nameTHEN
Sequence_of_statements1;
WHENexception_nameTHEN
Sequence_of_statements2;
[WHENOTHERSTHEN
Sequence_of_statements3;]
END;
每一个异常处理部分都是由WHEN子句和引发异常以后要执行的语句组成的。
WHEN标识这个处理子程序是针对哪个异常的。
OTHERS异常处理子程序
PL/SQL定义了一个异常处理子程序,即OTHERS。
当前异常处理部分定义的所有WHEN语句都没有处理的任意一个已引发的异常,都会导致执行这个OTHERS异常处理子程序。
该异常处理子程序应该总是作为代码块的最后一个异常处理子程序,这样就会首先扫描前面的异常处理子程序。
WHENOTHERS会捕获所有异常,不管这些异常是预定义的,还是用户自定义的。
检查错误堆栈—SQLCODE和SQLERRM
PL/SQL使用两个内置函数SQLCODE和SQLERRM提供错误信息。
SQLCODE返回的是当前的错误代号,而SQLERRM返回的是当前的错误信息文本。
如果是用户自定义的异常,SQLCODE就会返回值1,SQLERRM就会返回“User-definedException”。
下面是一个使用SQLCODE和SQLERRM的例子
viewplaincopytoclipboardprint?
DECLARE
--Exceptiontoindicateanerrorcondition
e_DuplicateAuthorsEXCEPTION;
--IDsforthreeauthors
v_Author1books.author1%TYPE;
v_Author2books.author2%TYPE;
v_Author3books.author3%TYPE;
--Codeandtextofotherruntimeerrors
v_ErrorCodelog_table.code%TYPE;
v_ErrorTextlog_table.message%TYPE;
BEGIN
/*FindtheIDsforthe3authorsof'Oracle9iDBA101'*/
SELECTauthor1,author2,author3
INTOv_Author1,v_Author2,v_Author3
FROMbooks
WHEREtitle='Oracle9iDBA101';
/*Ensurethattherearenoduplicates*/
IF(v_Author1=v_Author2)OR(v_Author1=v_Author3)OR
(v_Author2=v_Author3)THEN
RAISEe_DuplicateAuthors;
ENDIF;
EXCEPTION
WHENe_DuplicateAuthorsTHEN
/*Handlerwhichexecuteswhenthereareduplicateauthorsfor
Oracle9iDBA101.Wewillinsertalogmessagerecording
whathashappened.*/
INSERTINTOlog_table(info)
VALUES('Oracle9iDBA101hasduplicateauthors');
WHENOTHERSTHEN
/*Handlerwhichexecutesforallothererrors.*/
v_ErrorCode:
=SQLCODE;
--NotetheuseofSUBSTRhere.
v_ErrorText:
=SUBSTR(SQLERRM,1,200);
INSERTINTOlog_table(code,message,info)VALUES
(v_ErrorCode,v_ErrorText,'Oracleerroroccurred');
END;
/
由于该堆栈上每一条错误消息文本的最大长度均为512个字节,但是堆栈中可能会有多条消息文本。
在上面的例子中,v_ErrorText只有200个字符。
如果该错误消息文本长度大于200个字符,那么赋值语句
v_ErrorText:
=SQLERRM;
就会引发预定义的异常VALUE_ERROR。
为了防止发生这种异常,我们使用了内置函数SUBSTR。
注意,SQLCODE和SQLERRM的返回值首先会被分配给局部变量,然后再在SQL语句中使用这些局部变量。
因为这些函数都是过程化的函数,所以不能直接在SQL语句中使用它们。
通过下面这个例子我们看看错误号和相应的错误消息文本之间的关系
viewplaincopytoclipboardprint?
setserveroutputon
BEGIN
DBMS_OUTPUT.PUT_LINE('SQLERRM(0):
'||SQLERRM(0));
DBMS_OUTPUT.PUT_LINE('SQLERRM(100):
'||SQLERRM(100));
DBMS_OUTPUT.PUT_LINE('SQLERRM(10):
'||SQLERRM(10));
DBMS_OUTPUT.PUT_LINE('SQLERRM:
'||SQLERRM);
DBMS_OUTPUT.PUT_LINE('SQLERRM(-1):
'||SQLERRM(-1));
DBMS_OUTPUT.PUT_LINE('SQLERRM(-54):
'||SQLERRM(-54));
END;
/
--运行结果如下
SQL>@SQLERRM.sql
SQLERRM(0):
ORA-0000:
normal,successfulcompletion
SQLERRM(100):
ORA-01403:
nodatafound
SQLERRM(10):
-10:
non-ORACLEexception
SQLERRM:
ORA-0000:
normal,successfulcompletion
SQLERRM(-1):
ORA-00001:
uniqueconstraint(.)violated
SQLERRM(-54):
ORA-00054:
resourcebusyandacquirewithNOWAITspecified
PL/SQLproceduresuccessfullycompleted.
setserveroutputon
BEGIN
DBMS_OUTPUT.PUT_LINE('SQLERRM(0):
'||SQLERRM(0));
DBMS_OUTPUT.PUT_LINE('SQLERRM(100):
'||SQLERRM(100));
DBMS_OUTPUT.PUT_LINE('SQLERRM(10):
'||SQLERRM(10));
DBMS_OUTPUT.PUT_LINE('SQLERRM:
'||SQLERRM);
DBMS_OUTPUT.PUT_LINE('SQLERRM(-1):
'||SQLERRM(-1));
DBMS_OUTPUT.PUT_LINE('SQLERRM(-54):
'||SQLERRM(-54));
END;
/
EXCEPTION_INITpragma
你可以将一个经过命名的异常和一个特别的ORACLE错误相关联。
这会使你专门能够捕获此错误,而不是通过WHENOTHERS处理器来进行捕获。
EXCEPTION_INITpragma的语法如下:
PRAGMAEXCEPTION_INIT(exception_name,Oracle_error_number);
这里,exception_name是在PRAGMA前面声明的异常的名字,而Oracle_error_number是与此命名异常相关的所需错误代码。
这个PRAGMA必须在声明部分。
下面这个例子在运行时刻如果遇到“ORA-1400:
mandatoryNOTNULLcolumnmissingorNULLduringinsert”错误时将引发e_MissingNull--用户定义的异常。
viewplaincopytoclipboardprint?
DECLARE
e_MissingNullEXCEPTION;
PRAGMAEXCEPTION_INIT(e_MissingNull,-1400);
BEGIN
INSERTINTOstudents(id)VALUES(NULL);
EXCEPTION
WHENe_MissingNullthen
INSERTINTOlog_table(info)VALUES('ORA-1400occurred');
END;
/
DECLARE
e_MissingNullEXCEPTION;
PRAGMAEXCEPTION_INIT(e_MissingNull,-1400);
BEGIN
INSERTINTOstudents(id)VALUES(NULL);
EXCEPTION
WHENe_MissingNullthen
INSERTINTOlog_table(info)VALUES('ORA-1400occurred');
END;
/
每次发生PRAGMAEXCEPTION_INIT时,一个Oracle错误只能和一个用户自定义异常相关联。
在异常处理内部,SQLCODE和SQLERRM将会返回发生Oracle错误的代码和错误消息,但是不会返回用户定义的消息。
使用RAISE_APPLICATION_ERROR
你可以使用内置函数RAISE_APPLICATION_ERROR以创建自己的错误消息,这可能要比已命名的异常更具说明性。
用户定义消息从块中传递到调用环境中的方式和ORACLE错误是一样的。
语法如下:
RAISE_APPLICATION_ERROR(error_number,error_message,[keep_errors]);
error_number是从-200000到-20999之间的参数,error_message是与此错误相关的正文,不能多于512个字节。
而keep_errors是一个布尔值,是可选的,如果为TRUE,那么新的错误将被添加到已经引发的错误列表中(如果有的话)。
如果为FALSE(这是缺省的设置),那么新的错误将替换错误的当前列表。
例如下面的这个例子将在为一个新的学生注册以前检查是否在班级中有足够的地方容纳他。
viewplaincopytoclipboardprint?
CREATEORREPLACEPROCEDURERegister(
/*Registersthestudentidentifiedbythep_StudentIDparameterintheclass
identifiedbythep_Departmentandp_Courseparameters.Beforecalling
ClassPackage.AddStudent,whichactuallyaddsthestudenttotheclass,this
procedureverifiesthatthereisroomintheclass,andthattheclass
exists.*/
p_StudentIDINstudents.id%TYPE,
p_DepartmentINclasses.department%TYPE,
p_CourseINclasses.course%TYPE)AS
v_CurrentStudentsNUMBER;--Currentnumberofstudentsintheclass
v_MaxStudentsNUMBER;--Maximumnumberofstudentsintheclass
BEGIN
/*Determinethecurrentnumberofstudentsregistered,andthemaximum
numberofstudentsallowedtoregister.*/
SELECTcurrent_students,max_students
INTOv_CurrentStudents,v_MaxStudents
FROMclasses
WHEREcourse=p_Course
ANDdepartment=p_Department;
/*Makesurethereisenoughroomforthisadditionalstudent.*/
IFv_CurrentStudents+1>v_MaxStudentsTHEN
RAISE_APPLICATION_ERROR(-20000,'Can''taddmorestudentsto'||
p_Department||''||p_Course);
ENDIF;
/*Addthestudenttotheclass.*/
ClassPackage.AddStudent(p_StudentID,p_Department,p_Course);
EXCEPTION
WHENNO_DATA_FOUNDTHEN
/*Classinformationpassedtothisproceduredoesn'texist.Raiseanerror
toletthecallingprogramknowofthis.*/
RAISE_APPLICATION_ERROR(-20001,p_Department||''||p_Course||
'doesn''texist!
');
ENDRegister;
/
5、异常的传播
1)在执行部分引发的异常
当一个异常是在块的执行部分引发的,PL/SQL使用下面的方法决定要激活哪个异常处理器:
如果当前块对该异常设置了处理器,那么执行它并成功完成该块的执行,然后控制会转给包含块。
如果当前块没有对当前异常定义处理器,那么通过在包含块中引发它来传播异常。
然后对包含块执行步骤一。
2)在声明部分引发的异常
如果在声明部分的赋值操作引发了一个异常,那么该异常将立即传播给包含块。
发生这种情况以后,在前面给出的法则将进一步被应用到异常的传播上。
尽管在当前块中有一个处理器,它也不会被执行。
3)在异常处理部分引发的异常
在异常处理器中也可能引发异常,这可以是通过RAISE语句显式引发的,也可以是由运行时刻错误隐含引发的。
无论怎样,异常都立即被传播给包含块,这和声明部分引发的异常相类似。
6、使用异常的准则
1)异常的范围
异常像变量一样,也是有一定范围的。
如果用户自定义异常传播到它的范围之外,就不能再通过名称引用它。
viewplaincopytoclipboardprint?
BEGIN
DECLARE
e_UserDefinedExceptionEXCEPTION;
BEGIN
RAISEe_UserDefinedException;
END;
EXCEPTION
/*e_UserDefinedExceptionisoutofscopehere-canonlybe
handledbyanOTHERShandler*/
WHENOTHERSTHEN
/*Justre-raisetheexception,whichwillbepropagatedtothe
callingenvironment*/
RAISE;
END;
/
一般而言,如果打算将用户自定义的错误传播到代码块之外,最好的方法就是在包中定义该异常,以使其在该代码块之外仍可见,或使用RAISE_APPLICATION_ERROR引发该异常。
如果创建一个成为GLOBALS的包,并在其中定义了一个e_UserDefinedException异常,那么这个异常在外部块中仍然可见。
如下例所示
viewplaincopytoclipboardprint?
CREATEORREPLACEPACKAGEGlobalsAS
/*Thispackagecontainsglobaldeclarations.Objectsdeclaredherewill
bevisibleviaqualifiedreferencesforanyotherblocksorprocedures.
Notethatthispackagedoesnothaveapackagebody.*/
/*Auser-definedexception.*/
e_UserDefinedExceptionEXCEPTION;
ENDGlobals;
/
--有了这个和GLOBALS包以后,就可以重写前面的代码:
BEGIN
BEGIN
RAISEGlobals.e_UserDefinedException;
END;
EXCEPTION
/*Sincee_UserDefinedExceptionisstillvisible,wecanhandleit
explicitly*/
WHENGlobals.e_UserDefinedExceptionTHEN--引用包中定义异常
/*Justre-raisetheexception,whichwillbepropagatedtothe
callingenvironment*/
RAISE;
END;
/
2)避免未处理的异常
优秀的编程经验是在整个程序中避免出现任何未经过处理的异常。
这可以通过在程序的最顶层使用一个OTHERS子程序来实现。
该处理子程序可以只登记错误并登记错误发生的位置,通过这种方法,就可以保证每个错误都会得到检查。
如下例所示
viewplaincopytoclipboardprint?
DECLARE
v_errornumbernumber;
v_errortextvarchar2(200);
Begin
…
EXCEPTION
WHENOTHERSTHEN
v_errornumber:
=SQLCODE;
v_errortext:
=SUBSTR(SQLERRM,1,200);
INSERTINTOlog_table(code,message,info)
VALUES
(v_errornumber,v_errortext,’Oracleerroroccurredat’||TO_CHAR(SYSDATE,’DD-MON-YYHH24:
MI:
SS’));
END;
3)标识错误发生的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- PLSQLexception 异常 处理
![提示](https://static.bdocx.com/images/bang_tan.gif)