1、PL/SQL错误类型

错误类型

报告者

处理方法

编译时错误

PL/SQL编译器

交互式地处理:编译器报告错误,你必须更正这些错误

运行时错误

PL/SQL运行时引擎

程序化地处理:异常由异常处理子程序引发并进行捕获

2、异常的声明

有两种异常:用户自定义异常和预定义异常

用户自定义异常就是由程序员自己定义的一个错误。该错误还不是非常重要,所以并没有将整个错误包含在Oracle的错误中。例如,它可能是一个与数据有关的错误。而预定义异常则对应于一般的SQL和PL/SQL错误。

用户自定义异常是在PL/SQL块的声明部分声明的。像变量一样,异常也有一个类型(EXCEPTION)和有效范围。例如:

  1. DECLARE
  2. Exception_name EXCEPTION;

3、异常的引发

与异常相关联的错误发生的时候,就会引发相应的异常。用户自定义异常是通过RAISE语句显式引发的,而预定义异常则是在它们关联的ORACLE错误发生的时候隐式引发的。如果发生了一个还没有和异常进行关联的ORACLE错误的时候,也会引发一个异常。该异常可以使用OTHERS子程序进行捕获。预定义的异常也可以使用RAISE进行显式地引发,如果需要这样做的话。

  1. RAISE exception_name;

4、异常的处理

发生异常的时候,程序的控制就会转移到代码块的异常处理部分。异常处理部分是由异常处理子程序组成的,这些异常处理子程序可以是针对某些异常的,也可以是针对所有异常的。与该异常相关联的错误发生,并引发了该异常的时候,就会执行异常处理部分的代码。

异常处理部分的语法如下:

  1. EXCEPTION
  2. WHEN exception_name THEN
  3. Sequence_of_statements1;
  4. WHEN exception_name THEN
  5. Sequence_of_statements2;
  6. [WHEN OTHERS THEN
  7. Sequence_of_statements3;]
  8. END;

每一个异常处理部分都是由WHEN子句和引发异常以后要执行的语句组成的。WHEN标识这个处理子程序是针对哪个异常的。

OTHERS异常处理子程序

PL/SQL定义了一个异常处理子程序,即OTHERS。当前异常处理部分定义的所有WHEN语句都没有处理的任意一个已引发的异常,都会导致执行这个OTHERS异常处理子程序。该异常处理子程序应该总是作为代码块的最后一个异常处理子程序,这样就会首先扫描前面的异常处理子程序。WHEN OTHERS会捕获所有异常,不管这些异常是预定义的,还是用户自定义的。

检查错误堆栈—SQLCODE和SQLERRM

PL/SQL使用两个内置函数SQLCODE和SQLERRM提供错误信息。SQLCODE返回的是当前的错误代号,而SQLERRM返回的是当前的错误信息文本。如果是用户自定义的异常,SQLCODE就会返回值1,SQLERRM就会返回“ User-defined Exception”。

下面是一个使用SQLCODE和SQLERRM的例子

  1. DECLARE
  2. -- Exception to indicate an error condition
  3. e_DuplicateAuthors EXCEPTION;
  4. -- IDs for three authors
  5. v_Author1 books.author1%TYPE;
  6. v_Author2 books.author2%TYPE;
  7. v_Author3 books.author3%TYPE;
  8. -- Code and text of other runtime errors
  9. v_ErrorCode log_table.code%TYPE;
  10. v_ErrorText log_table.message%TYPE;
  11. BEGIN
  12. /* Find the IDs for the 3 authors of 'Oracle9i DBA 101' */
  13. SELECT author1, author2, author3
  14. INTO v_Author1, v_Author2, v_Author3
  15. FROM books
  16. WHERE title = 'Oracle9i DBA 101';
  17. /* Ensure that there are no duplicates */
  18. IF (v_Author1 = v_Author2) OR (v_Author1 = v_Author3) OR
  19. (v_Author2 = v_Author3) THEN
  20. RAISE e_DuplicateAuthors;
  21. END IF;
  22. EXCEPTION
  23. WHEN e_DuplicateAuthors THEN
  24. /* Handler which executes when there are duplicate authors for
  25. Oracle9i DBA 101.  We will insert a log message recording
  26. what has happened. */
  27. INSERT INTO log_table (info)
  28. VALUES ('Oracle9i DBA 101 has duplicate authors');
  29. WHEN OTHERS THEN
  30. /* Handler which executes for all other errors. */
  31. v_ErrorCode := SQLCODE;
  32. -- Note the use of SUBSTR here.
  33. v_ErrorText := SUBSTR(SQLERRM, 1, 200);
  34. INSERT INTO log_table (code, message, info) VALUES
  35. (v_ErrorCode, v_ErrorText, 'Oracle error occurred');
  36. END;
  37. /

由于该堆栈上每一条错误消息文本的最大长度均为512个字节,但是堆栈中可能会有多条消息文本。在上面的例子中,v_ErrorText只有200个字符。如果该错误消息文本长度大于200个字符,那么赋值语句

v_ErrorText := SQLERRM;

就会引发预定义的异常VALUE_ERROR。为了防止发生这种异常,我们使用了内置函数SUBSTR。

注意,SQLCODE和SQLERRM的返回值首先会被分配给局部变量,然后再在SQL语句中使用这些局部变量。因为这些函数都是过程化的函数,所以不能直接在SQL语句中使用它们。

通过下面这个例子我们看看错误号和相应的错误消息文本之间的关系

  1. set serveroutput on
  2. BEGIN
  3. DBMS_OUTPUT.PUT_LINE('SQLERRM(0): ' || SQLERRM(0));
  4. DBMS_OUTPUT.PUT_LINE('SQLERRM(100): ' || SQLERRM(100));
  5. DBMS_OUTPUT.PUT_LINE('SQLERRM(10): ' || SQLERRM(10));
  6. DBMS_OUTPUT.PUT_LINE('SQLERRM: ' || SQLERRM);
  7. DBMS_OUTPUT.PUT_LINE('SQLERRM(-1): ' || SQLERRM(-1));
  8. DBMS_OUTPUT.PUT_LINE('SQLERRM(-54): ' || SQLERRM(-54));
  9. END;
  10. /
  11. --运行结果如下
  12. SQL> @SQLERRM.sql
  13. SQLERRM(0): ORA-0000: normal, successful completion
  14. SQLERRM(100): ORA-01403: no data found
  15. SQLERRM(10):  -10: non-ORACLE exception
  16. SQLERRM: ORA-0000: normal, successful completion
  17. SQLERRM(-1): ORA-00001: unique constraint (.) violated
  18. SQLERRM(-54): ORA-00054: resource busy and acquire with NOWAIT specified
  19. PL/SQL procedure successfully completed.

EXCEPTION_INIT pragma

你可以将一个经过命名的异常和一个特别的ORACLE错误相关联。这会使你专门能够捕获此错误,而不是通过WHEN OTHERS处理器来进行捕获。EXCEPTION_INIT pragma的语法如下:

PRAGMA EXCEPTION_INIT(exception_name,Oracle_error_number);

这里,exception_name是在PRAGMA前面声明的异常的名字,而Oracle_error_number是与此命名异常相关的所需错误代码。这个PRAGMA必须在声明部分。

下面这个例子在运行时刻如果遇到“ORA-1400:mandatory NOT NULL column missing or NULL during insert”错误时将引发e_MissingNull--用户定义的异常。

  1. DECLARE
  2. e_MissingNull EXCEPTION;
  3. PRAGMA EXCEPTION_INIT(e_MissingNull, -1400);
  4. BEGIN
  5. INSERT INTO students (id) VALUES (NULL);
  6. EXCEPTION
  7. WHEN e_MissingNull then
  8. INSERT INTO log_table (info) VALUES ('ORA-1400 occurred');
  9. END;
  10. /

每次发生PRAGMA EXCEPTION_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(这是缺省的设置),那么新的错误将替换错误的当前列表。

例如下面的这个例子将在为一个新的学生注册以前检查是否在班级中有足够的地方容纳他。

  1. CREATE OR REPLACE PROCEDURE Register (
  2. /* Registers the student identified by the p_StudentID parameter in the class
  3. identified by the p_Department and p_Course parameters. Before calling
  4. ClassPackage.AddStudent, which actually adds the student to the class, this
  5. procedure verifies that there is room in the class, and that the class
  6. exists. */
  7. p_StudentID IN students.id%TYPE,
  8. p_Department IN classes.department%TYPE,
  9. p_Course IN classes.course%TYPE) AS
  10. v_CurrentStudents NUMBER;  -- Current number of students in the class
  11. v_MaxStudents NUMBER;      -- Maximum number of students in the class
  12. BEGIN
  13. /* Determine the current number of students registered, and the maximum
  14. number of students allowed to register. */
  15. SELECT current_students, max_students
  16. INTO v_CurrentStudents, v_MaxStudents
  17. FROM classes
  18. WHERE course = p_Course
  19. AND department = p_Department;
  20. /* Make sure there is enough room for this additional student. */
  21. IF v_CurrentStudents + 1 > v_MaxStudents THEN
  22. RAISE_APPLICATION_ERROR(-20000, 'Can''t add more students to ' ||
  23. p_Department || ' ' || p_Course);
  24. END IF;
  25. /* Add the student to the class. */
  26. ClassPackage.AddStudent(p_StudentID, p_Department, p_Course);
  27. EXCEPTION
  28. WHEN NO_DATA_FOUND THEN
  29. /* Class information passed to this procedure doesn't exist. Raise an error
  30. to let the calling program know of this. */
  31. RAISE_APPLICATION_ERROR(-20001, p_Department || ' ' || p_Course ||
  32. ' doesn''t exist!');
  33. END Register;
  34. /

5、异常的传播

1)在执行部分引发的异常

当一个异常是在块的执行部分引发的,PL/SQL使用下面的方法决定要激活哪个异常处理器:

如果当前块对该异常设置了处理器,那么执行它并成功完成该块的执行,然后控制会转给包含块。

如果当前块没有对当前异常定义处理器,那么通过在包含块中引发它来传播异常。然后对包含块执行步骤一。

2)在声明部分引发的异常

如果在声明部分的赋值操作引发了一个异常,那么该异常将立即传播给包含块。发生这种情况以后,在前面给出的法则将进一步被应用到异常的传播上。尽管在当前块中有一个处理器,它也不会被执行。

3)在异常处理部分引发的异常

在异常处理器中也可能引发异常,这可以是通过RAISE语句显式引发的,也可以是由运行时刻错误隐含引发的。无论怎样,异常都立即被传播给包含块,这和声明部分引发的异常相类似。

6、使用异常的准则

1)异常的范围

异常像变量一样,也是有一定范围的。如果用户自定义异常传播到它的范围之外,就不能再通过名称引用它。

  1. BEGIN
  2. DECLARE
  3. e_UserDefinedException EXCEPTION;
  4. BEGIN
  5. RAISE e_UserDefinedException;
  6. END;
  7. EXCEPTION
  8. /* e_UserDefinedException is out of scope here - can only be
  9. handled by an OTHERS handler */
  10. WHEN OTHERS THEN
  11. /* Just re-raise the exception, which will be propagated to the
  12. calling environment */
  13. RAISE;
  14. END;
  15. /

一般而言,如果打算将用户自定义的错误传播到代码块之外,最好的方法就是在包中定义该异常,以使其在该代码块之外仍可见,或使用RAISE_APPLICATION_ERROR引发该异常。如果创建一个成为GLOBALS的包,并在其中定义了一个e_UserDefinedException异常,那么这个异常在外部块中仍然可见。

如下例所示

  1. CREATE OR REPLACE PACKAGE Globals AS
  2. /* This package contains global declarations. Objects declared here will
  3. be visible via qualified references for any other blocks or procedures.
  4. Note that this package does not have a package body. */
  5. /* A user-defined exception. */
  6. e_UserDefinedException EXCEPTION;
  7. END Globals;
  8. /
  9. --有了这个和GLOBALS包以后,就可以重写前面的代码:
  10. BEGIN
  11. BEGIN
  12. RAISE Globals.e_UserDefinedException;
  13. END;
  14. EXCEPTION
  15. /* Since e_UserDefinedException is still visible, we can handle it
  16. explicitly */
  17. WHEN Globals.e_UserDefinedException THEN           --引用包中定义异常
  18. /* Just re-raise the exception, which will be propagated to the
  19. calling environment */
  20. RAISE;
  21. END;
  22. /

2)避免未处理的异常

优秀的编程经验是在整个程序中避免出现任何未经过处理的异常。这可以通过在程序的最顶层使用一个OTHERS子程序来实现。该处理子程序可以只登记错误并登记错误发生的位置,通过这种方法,就可以保证每个错误都会得到检查。

如下例所示

  1. DECLARE
  2. v_errornumber number;
  3. v_errortext varchar2(200);
  4. Begin
  5. EXCEPTION
  6. WHEN OTHERS THEN
  7. v_errornumber:=SQLCODE;
  8. v_errortext:=SUBSTR(SQLERRM,1,200);
  9. INSERT INTO log_table(code,message,info)
  10. VALUES
  11. (v_errornumber,v_errortext,’Oracle error occurred at’||TO_CHAR(SYSDATE,’DD-MON-YY HH24:MI:SS’));
  12. END;

3)标识错误发生的位置

由于整个代码块都使用同一个异常处理部分检查并处理异常,所以很难确定引发这个错误的是哪一条SQL语句。考虑下面示例

  1. BEGIN
  2. SELECT…
  3. SELECT..
  4. SELECT…
  5. EXCEPTION
  6. WHEN NO_DATA_FOUND THEN
  7. --which select statement raised the exception?
  8. END;
  9. --解决上述问题的方法有两种。第一种是添加一个标识该SQL语句的计数器:
  10. DECLARE
  11. V_selectcounter NUMBER:=1;
  12. BEGIN
  13. SELECT…
  14. V_selectcounter NUMBER:=2;
  15. SELECT…
  16. V_selectcounter NUMBER:=3;
  17. SELECT…
  18. EXCEPTION
  19. WHEN NO_DATA_FOUND THEN
  20. INSERT INTO log_table(info) VALUES(‘NO DATA FOUND IN SELECT’||v_selectcounter);
  21. END;
  22. --另一种方法是将每一条语句都放置在它自己的子块中:
  23. BEGIN
  24. BEGIN
  25. SELECT…
  26. EXCEPTION
  27. WHEN NO_DATA_FOUND THEN
  28. INSERT INTO log_table(info) VALUES(‘NO DATA FOUND IN SELECT 1’);
  29. END;
  30. BEGIN
  31. SELECT…
  32. EXCEPTION
  33. WHEN NO_DATA_FOUND THEN
  34. INSERT INTO log_table(info) VALUES(‘NO DATA FOUND IN SELECT 2’);
  35. END;
  36. BEGIN
  37. SELECT…
  38. EXCEPTION
  39. WHEN NO_DATA_FOUND THEN
  40. INSERT INTO log_table(info) VALUES(‘NO DATA FOUND IN SELECT 3’);
  41. END;
  42. END;

7、异常代码的编写风格

1)RAISE_APPLICATION_ERROR和RAISE的比较

RAISE_APPLICATION_ERROR

RAISE

允许我们填写自己的错误消息文本,该文本可以包含应用程序专用的数据

不允许包含消息文本

不能使用已命名的异常处理子程序进行捕获,只能使用OTHERS处理子程序进行捕获

可以使用已命名的处理子程序捕获该异常,只要该异常在它自己的异常范围内即可

通常而言,推荐对设计给终端用户看的错误,使用RAISE_APPLICATION_ERROR。因为对于他们而言,具体的错误编号和描述性文本非常有用。而另一方面,对设计为由程序直接进行处理的异常,推荐使用RAISE。

2)将异常作为控制语句

因为引发异常会将程序的控制逻辑转移到代码块的异常处理部分,所以可以将RAISE语句用作控制语句,就像GOTO语句一样。例如,如果我们有很深的嵌套循环,并需要立即从中退出的时候,这可能会非常有用。

http://blog.csdn.net/wanghai__/article/details/4736501

ORACLE PL/SQL异常处理(Exception)学习笔记的更多相关文章

  1. Oracle PL/SQL语句基础学习笔记(上)

    PL/SQL是ORACLE对标准数据库语言的扩展,ORACLE公司已经将PL/SQL整合到ORACLE server和其它工具中了,近几年中很多其它的开发者和DBA開始使用PL/SQL,本文将讲述PL ...

  2. 初级Oracle和SQL学习者的学习笔记。韩顺平-玩转oracle。

    我自己就是一个oracle和sql的初学者,前段时间看了韩顺平老师的oracle视频教程,觉得很深入浅出,收获了很多.同时自己也做了不少笔记,现在想将纸质笔记以自己的话总结出来.俗话说得好:教学总是相 ...

  3. PL/SQL语言的学习笔记

    一.PL/SQL简介1.什么是PL/SQL程序?(PL/SQL是对SQL语言的一个扩展,从而形成的一个语言) 2.PL/SQL语言的特点(操作Orcale数据库效率最高的就是PL/SQL语言,而不是C ...

  4. Oracle PL/SQL 异常处理

    Oracle数据库中的异常:没有异常的转移,因为没有受检异常和非受检异常得区分. 1.异常的产生: 2.异常的处理: declare --变量定义,初始化赋值. begin --变量的赋值,函数调用, ...

  5. [推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼、百战不殆)

    原文:[推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼.百战不殆) [推荐]ORACLE PL/SQL编程之五: 异常错误处理(知已知彼.百战不殆) 继上三篇:ORACLE PL/S ...

  6. Oracle数据库之PL/SQL异常处理

    Oracle数据库之PL/SQL异常处理 异常指的是在程序运行过程中发生的异常事件,通常是由硬件问题或者程序设计问题所导致的. PL/SQL程序设计过程中,即使是写得最好的程序也可能会遇到错误或未预料 ...

  7. [强烈推荐]ORACLE PL/SQL编程详解之七:程序包的创建与应用(聪明在于学习,天才在于积累!)

    原文:[强烈推荐]ORACLE PL/SQL编程详解之七:程序包的创建与应用(聪明在于学习,天才在于积累!) [强烈推荐]ORACLE PL/SQL编程详解之七: 程序包的创建与应用(聪明在于学习,天 ...

  8. oracle pl/sql 程序设计 历史笔记整理

    20131016 周三 oracle pl/sql 程序设计 第2章 创建并运行pl/sql代码 sqlplus yjkhecc/yjkhecc@10.85.23.92:1521/orcl 在java ...

  9. 浅析Oracle PL/SQL 学习--未完待续

    这是一篇关于Oracle Pl/SQL数据库编程的课程学习分享... 首先说明几点: 学习这门课程之前,已经学过并且掌握一些基础的SQL语句.数据库结构分析.ER图设计等知识: 这里也只是较为大概地将 ...

随机推荐

  1. [Redux] Refactoring the Entry Point

    We will learn how to better separate the code in the entry point to prepare it for adding the router ...

  2. android 35 ListView增删改差

    MainActivity package com.sxt.day05_11; import java.util.ArrayList; import java.util.List; import and ...

  3. 全面分析 Spring 的编程式事务管理及声明式事务管理--转

    开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本 ...

  4. linux下杀死进程(kill)的N种方法 【转】

    转自 http://blog.csdn.net/andy572633/article/details/7211546 首先,用ps查看进程,方法如下: $ ps -ef ……smx       182 ...

  5. ASP.NET Boilerplate Castle容器无缝添加日志功能

    以添加log4net日志框架为例进行讲解 1.通常log4net的配置参数放在单独的配置文件中,但也可以写在web.config中,这里在我们的web项目中添加log4net.config应用配置文件 ...

  6. Java方法-字符串

    [Java字符串] 通过字符串函数 compareTo (string) ,compareToIgnoreCase(String) 及 compareTo(object string) 来比较两个字符 ...

  7. static 方法.

    If a subclass defines a static method with the same signature as a static method in the superclass, ...

  8. 某PHP代码加密

    <?php /* 本程序已加密: 2014-11-15 10:10:11 */ xs_run('JGxosS9QplmqLA6qjYo/LiX5ecUe0DH7p42Ww/Mdkf5/ybZDs ...

  9. 最终版-perl工具解析数据库的报告文件0120

    ********************需要根据自己的实际环境修改哦**************************** ******************** 1. 收集awr报告样本   a ...

  10. Cocos2dx开发(1)——Win8.1下 NDK r10 环境搭建

    内容简要:仅讲述NDK在Windows环境下搭建方法,至于NDK何物一概不属于本文内容,老鸟或已有环境的跳过. 笔者已安装的环境: vs2013企业版.谷歌官网adt 22.3.0(推荐)省得自己ec ...