如果 PLSQL发生了错误, 无论是系统错误还是应用错误, 都会抛出一个异常, 当前 PL/SQL 块中执行单元会暂停处理, 如果当前块有一个异常处理单元的话, 控制会转移到当前块的异常处理单元来处理异常, 完成了异常处理后就不能再返回到当前块, 相反, 控制会转移到外层包伟块, 如果有的话.

在Pl/sql 中, 任何类型的错误都按程序异常处理:

  • 系统产生错误( 内存溢出, 索引重复等等 )
  • 用户动作导致的错误
  • 应用程序发出的警告

种类: 系统异常 和 程序员定义的异常

抛出: 数据库本身可以抛出异常, 自己写指令 RAISE_APPLICATION_ERROR 命令也可以抛出异常.

传播: 一个异常如果没有在当前块内被处理了, 而是从当前块传给包围它的外层块的过程.

如果是没有名字的异常, 那么可以使用 ( ORA-01843)

如果 pl/sql 发生了一个错误, 无论是系统错误还是应用错误, 都会抛出一个异常, 当前 pl/sql 块中执行单元就会暂停处理, 如果当前块有一个异常处理单元的话, 控制会转移到当前块的异常处理单元来处理异常, 完成了异常处理后就不能再返回到当前块, 相反, 控制会转移到外层包围块, 如果有的话.

被命名的系统异常

声明有名异常

pl/sql 在 standard 包中声明的异常(还有其他内置包中的异常)已经覆盖了内部或系统生成的错误. 但是用户在应用程序中遇到的问题很多都是应用程序特有的, 即应用程序的逻辑错误, 比如 账户余额为负数, 或者赎回日期不能是过去日期 等等, 这种逻辑错误的异常, 你可以定义并自己抛出, 注意: 这不是系统错误, 系统不会因此而抛出异常.

EXCEPTION

WHEN OTHERS THEN

IF SQLCODE = –1843 THEN

这个代码很难理解, 可以使用 EXCEPTION_INIT, 作用是将自己定义的名称 和 错误代码相联系

EXCEPTION_INIT 是一个在编译时刻执行的命令或者叫编译命令.

PROCEDURE my_procedure

IS

invalid_month EXCEPTION;   -- 定义一个异常

PRAGMA EXCEPTION_INIT ( invalid_month, –1843);  -- 建立异常名称与错误代码之间的联系

BEGIN

EXCEPTION

WHEN invalid_month THEN

异常的作用范围

被命名的系统异常: 例如 no_data_found, 全局可用的异常

被命名的程序定义的异常: 这种异常只在声明它的块(以及所有嵌套块)的执行单元, 异常处理单元被抛出或处理. 对于包规范部分定义的异常, 那么对于那些对这个包具有 execute 权限的用户的程序, 这个异常都是可见的.

匿名系统异常: 这些异常在任何 pl/sql 块的异常处理单元 when others 部分处理. 如果这些已经被指定了名字, 这个名称的作用范围是和被命名的自定义异常时一样的, 注意: 是这个名称的范围.

匿名自定义异常: 这些异常只是在调用 raise_application_error 时才定义, 并返回到调用程序.

抛出异常的方式

RAISE exception_name;

RAISE package_name.exception_name;  ( 如果异常在包中, 且不是 standard 包, 那么就要先使用包名 )

RAISE;

RAISE_APPLICATION_ERROR( 也是抛出异常, 也是在DBMS_STANDARD 这个包中, 这个命令有个好处是, 可以准许你写一些错误信息 )

PROCEDURE RAISE_APPLICATION_ERROR (

num binary_integer,  -- 错误号, –20999 ~ -20000

msg varchar2,        -- 错误消息

keeperrorstack boolean default false );  -- 表明是否想把这个错误添加到栈中(true)还是替换现存的错误(false)

捕获异常

EXCEPTION

WHEN exception_name

THEN

executable statements

WHEN OTHERS

THEN

executable statements

异常例子1, 直接使用 raise

 procedure my_procedure
is
invalid_month exception;
begin
raise invalid_month;
exception
when invalid_month then
dbms_output.put_line('invalid_month error');
end;
 -- chap06_02.sql
procedure my_procedure
is
invalid_month exception;
pragma exception_init(invalid_month, -20188);
begin
raise_application_error(-20188, 'You get an exception');
-- 注意此处的抛出异常的message, 如果此异常被捕获了, 那么这个message是不会被显示出来的, 而直接走捕获异常的程序 exception
when invalid_month then
dbms_output.put_line('invalid_month error');
end;

内置错误函数

SQLCODE, 返回一个错误代码, 如果没有错误, 返回 0

SQLERRM, 可以返回错误代码对应的官方的错误信息.

要想查看某个错误号对应的提示信息, 可以使用如下:

begin

dbms_output.put_line(SQLERRM(-1403));

end;

/

DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, 10g 以后开始引入, 返回一个格式化文本串, 能够回退到错误最初发生代码行的行号.

未处理的异常

如果抛出的异常没有被捕获, 那么就是一个未处理异常, PLSQL会把这个未处理的异常作为错误代码一直返回到运行PLSQL的应用环境, 如果是 sql*plus, 所有顶层块逻辑中的DML都会自动的执行 ROLLBACK.

防止未处理异常: ( 设计最外层块或程序 )

  • 捕获任何可能传播的异常
  • 为错误记录日志, 从而能够分析出了什么情况出现的异常
  • 返回一个状态码, 描述或其他信息, 以帮助宿主环境决定采取适当的措施.

异常会逐层向外传播

如果想抛出异常以后, 程序还能继续运行, 就需要将各个模块独立出来, 针对自己的独立的模块, 进行异常处理, 这样并不影响其他模块. 如果是嵌套类型, 并且捕获异常程序在最外层的话, 那么只要内层有一个错误, 将导致所有的操作全部ROLLBACK.

如何使用 WHEN OTHERS

when others 可以捕获所有未捕获的异常, 因为你没有明确指出对某些特殊异常的处理, 你可能要利用内置函数 SQLCODE 等来确认到底抛出的是什么异常.

个人感觉, 首先想好逻辑关系, 如果使用 when others, 那么就要打印出逻辑号, 或将其保存起来. 尽量少用 when others

 procedure add_company(
id_in IN company.id%type,
name_in IN company.name%type,
type_id_in IN company.type_id%type)
is
begin
dbms.output_putline('show others exceptions');
exception
when others
then
/*
|| 位于异常中的匿名块,
|| 我可以声明本地变量来放错误号和错误信息
*/
declare
l_errcode PLS_INTEGER := sqlcode;
begin
case l_errcode
when -1 then
dbms_output.put_line('error 1');
raise; -- 这里的 raise 意思是虽然捕获了异常, 但是你只要知道是哪个异常, 但是还是要抛出这个异常
-- 为的就是, 程序本身要抛出来, 而如果你不加这个 raise, 那么即便有异常, 程序也会显示成功
-- 因为你已经捕获了异常, 而实际上, 你是用 when others 捕获的, 并非真正捕获.
when -2291 then
dbms_output.put_line('error 2');
raise;
else
raise;
end case;
end;
end add_company;

设计异常的框架

  • 当代码发生错误时, 要尽可能多的获得错误发生的环境信息
  • 尽量避免用像 when error then Null (或更差的 when others then null) 这样掩盖错误
  • 如果可能, 尽量使用 pl/sql 的缺省错误处理机制. 要避免程序向外层调用块或者宿主环境返回状态代码.

异常的分类( 人为分类 )

deliberate: 代码本身可能需要依赖异常, 必须设计有异常放生

例如, utl_file.get_line, 这是一个读取文件的函数, 每次读取一行, 但是每次读取超过最后一样, 就会抛出 no_data_found, 这就是它本身的工作方式

 procedure read_file_and_do_stuff (
dir_in in varchar2, file_in in varchar2 )
is
l_file utl_file.file_type;
l_line varchar2(32767);
begin
l_file := utl_file.fopen (dir_in, file_in, 'R', max_linesize => 32767); loop
utl_file.get_line(l_file, l_line);
do_stuff;
end loop;
exception
-- 作为一个正常的处理过程来处理, 意义上并不是异常
when no_data_found
then
utl_file.fclose(l_file);
more_stuff_here;
end;
/
show errors;

针对这种异常: 你要预先为这种异常写好代码, 最佳实践就是避免在异常处理单元包含逻辑. 异常处理单元应该只包含处理错误需要的代码, 把错误记录日志, 重新抛出异常等.

unfortunate: 这是一个错误, 但是属于在意料之中并不意味着有问题, 例如: select into 语句发生了 no_data_found 异常.

针对这种异常: 这种异常是, 并不把这种情况看做是错误, 也没有按未处理异常传播, 这时, 通过返回一个值或者返回状态标志的方法来只是发生了异常, 应该让程序的用户自行去决定是否应该为这个错误终止程序.

unexcepted: 这是一个真正的错误, 应用程序出了问题. 一个例子就是 select into 应该返回一条记录, 但是却抛出了 too_many_rows异常.

针对这种异常: 尽可能的记录下应用程序的上下文信息, 以便帮助找到发生原因, 程序本身也应该因为抛出未处理异常而终止.

pl/sql programming 06 异常处理的更多相关文章

  1. pl/sql programming 15 数据提取

    数据提取 -- 游标 游标只是一个指向某个结果集的指针. 声明游标: cursor employee_cur IS select * from employees; 打开游标: open employ ...

  2. pl/sql programming 03 语言基础

    PL/SQL 块结构 最小的有意义的代码单元叫做 块(block). 一个块是一组代码, 这个块给出了执行边界, 也为变量声明和异常处理提供了作用范围, pl/sql 准许我们创建匿名块和命名块, 命 ...

  3. pl/sql programming 02 创建并运行plsql代码

    /* * chap 02 * ------------------------------------------------- */ create or replace function wordc ...

  4. PL/SQL Select into 异常处理

    在使用select into 为变量赋值时,如果变量是集合类型,不会产生异常,而如果是基本类型或记录类型,则会报异常. 异常产生了怎么办?当然是捕获并处理啦. 对于普通的代码块来说,在代码块的结尾处理 ...

  5. pl/sql programming 05 循环迭代处理

    使用循环应考虑的因素 1. 循环什么时候结束 2. 什么时候测试是否该结束循环 3. 采用这种循环的原因 1. 普通循环(简单循环) 使用场合, 不能确定循环执行多少次, 要求循环至少执行一次. 另外 ...

  6. PL/SQL基础-异常处理

    --*********异常处理一.异常的类型 ORACLE异常分为两种类型:系统异常.自定义异常. 其中系统异常又分为:预定义异常和非预定义异常.1.预定义异常 ORACLE定义了他们的错误编号和异常 ...

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

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

  8. Oracle 学习笔记 17 -- 异常处理(PL/SQL)

    程序异常是在操作期间正常,出现在节目的准备过程是不可避免的例外.但是,必须有一个相应的异常处理机 制,以保证程序的正常运行.PL/SQL程序运行过程中出现的错误.称为异常. 一个优秀的程序都应该可以正 ...

  9. PL/SQL编程基础(五):异常处理(EXCEPTION)

    异常处理 异常产生所带来的问题: 使用EXCEPTION程序块进行异常处理: 实现用户自定义异常. 使用异常可以保证在程序中出现运行时异常时程序可以正常的执行完毕: 用户可以使用自定义异常进行操作. ...

随机推荐

  1. vsm shadowmap format

    遇到个奇怪的问题. 在做vsm ,shadowmap format RGBA8 结果正常 RGBA16F 场景不形成阴影的地方变纯黑,因为sm里面这些地方变纯黑(感觉这个好修一些) RGBA32F 阴 ...

  2. 学习NAnt Build .CS+Solution+MSBuild+SVN+NUnit+NUnitReport

    NAnt help:http://nant.sourceforge.net/release/latest/help/tasks/NAntContrib help:http://nantcontrib. ...

  3. 零成本实现WEB性能测试(一)性能测试基础

    1.1 初识性能测试 概念:负载测试&压力测试. 目的:评估系统的能力,识别系统弱点,系统调优,检测问题,验证稳定性. 分类:负载测试,压力测试,容量测试 B/S指标: Avg Rps,平均每 ...

  4. 索引服务混战ASP.NET――微软的又一个隔离墩

    话说月初笔者在华山之巅搞定了ASP.NET一起莫名奇妙的异常,自此之后和公主过着…嘟--,不好意思,书都看杂了,串了台了.好,咱们闲言少叙,书归正传. 自从上次解决了由调试文件库引起的ASP.NET执 ...

  5. 【译】使用 Python 编写虚拟机解释器

    [译]如何使用 Python 创建一个虚拟机解释器? 原文地址:Making a simple VM interpreter in Python 更新:根据大家的评论我对代码做了轻微的改动.感谢 ro ...

  6. Delphi中关于资源释放(Free,Relealse,FreeAndNil)

    根据日常编程经验,得出一些Delphi中关于资源释放的体会. 假如有对象Obj为TObject类型: 1) Obj.Free直接释放资源后,调用OnDestroy事件,但是没有将Obj指针值置为Nil ...

  7. HDU4831&&4832&&4834

    好久没打代码啦,今天lu一发百度之星,感觉还是学到不少东西的,写点收获. 第一题就是现在的HDU4831啦,题意很清楚,我一开始以为休息区也可以变为风景区,所以就不敢敲了,后来才得知数据里只会改风景区 ...

  8. 转载:PHP,MySQL的安装与配置

    本文转自:http://www.cnblogs.com/janas/archive/2012/08/27/2659240.html 一.安装配置PHP 1.下载Php的版本zip包之后,解压缩到指定目 ...

  9. Android的Testing和Instrumentation

    Android提供了一系列强大的测试工具,它针对Android的环境,扩展了业内标准的JUnit测试框架.尽管你可以使用JUnit测试Android工程,但Android工具允许你为应用程序的各个方面 ...

  10. FastReport报表

    http://www.fastreportcn.com/product/FASTREPORT_dotNET.html