PL/SQL 训练03 --异常
--程序员在开发的时候,经常天真的认为这个世界是完美的,用户如同自己般聪明,总能按照自己设想的方式
--操作系统输入数据。但残酷的事实告诉我们,这是不可能的事情,用户总会跟我们相反的方式操作系统
--于是,无数的问题砸向我们。
--那么怎么办呢?
---PL/SQL提供了强大、灵活的错误捕获和处理方法,这节课我们来领略她的风采
--什么是异常?先看一个例子
- DECLARE
- V_DIV NUMBER := 20;
- V_NUM NUMBER := 0;
- BEGIN
- DBMS_OUTPUT.put_line(V_DIV/V_NUM);
- END ;
- /
--在plsql中,任何类型的错误都可按程序异常统一对待,可能是
--系统产生的错误,比如内存溢出,或者索引出现重复值
--用户动作导致的错误
--应用程序向用户发出的警告
--异常处理单元
- DECLARE
- V_DIV NUMBER := 20;
- V_NUM NUMBER := 0;
- BEGIN
- DBMS_OUTPUT.put_line(V_DIV/V_NUM);
- exception
- when standard.zero_divide then
- DBMS_OUTPUT.put_line('除数为零');--异常处理单元
- END ;
- /
--异常分类
--系统定义异常:oracle定义的异常,在PLSQL运行时引擎发现某个错误跑出来的异常,比如NO_DATA_FOUND
--程序员自定义异常:程序员定义的异常,专门针对当前的应用程序。
--可使用EXCEPTION N_INIT指定错误名称,或者使用RAISE_APPILICATION_ERROR给错误指定一个数字和描述
--异常的定义
--声明有名异常
- exception_name EXCEPTION;
- DECLARE
- is_used_true EXCEPTION; --声明异常名称
- is_used_false exception;
- v_bool boolean := true;
- BEGIN
- if v_bool then
- raise is_used_true;
- else
- raise is_used_false;
- end if;
- EXCEPTION
- WHEN is_used_true THEN --捕获异常
- DBMS_OUTPUT.put_line('捕获异常is_used_true');
- when is_used_false then
- DBMS_OUTPUT.put_line('捕获异常is_used_false');
- END;
- /
--异常名称和错误代码相关联
--oracle只给一部分异常定义了名字,还有上千个其它错误只有错误数字和消息
--也可以使用使用RAISE_APPlication_ERROR抛出只有错误数字(-20999,-20000)和错误信息的异常,比如
- DECLARE
- v_bool boolean := true;
- BEGIN
- if v_bool then
- raise_application_error(-20000, 'test true');
- else
- raise_application_error(-20001, 'test FALSE');
- end if;
- EXCEPTION
- WHEN OTHERS THEN
- --捕获异常
- DBMS_OUTPUT.put_line(SQLERRM);
- IF SQLCODE = -20000 THEN --SQLCODE是一个内置函数,返回最后抛出错误的数值
- DBMS_OUTPUT.put_line('IT IS TRUE ERROR');
- ELSIF SQLCODE = -20001 THEN
- DBMS_OUTPUT.put_line('IT IS FALSE ERROR');
- END IF;
- END;
- /
- --EXCEPTION_INIT可以将用EXCEPTION定义的异常名称和一个指定的错误数字关联到一起,一旦创建了关联关系
- --可以通过名字抛出异常
- declare
- is_used_true EXCEPTION;
- is_used_false EXCEPTION;
- pragma exception_init(is_used_true, -20000);
- pragma exception_init(is_used_false, -20001);
- --关联的数字不能是-1403
- --不可以使用0或者100之外的任何正数
- --不可以是小于-1000000的负数
- v_bool boolean := false;
- begin
- if v_bool then
- raise_application_error(-20000, 'test true');
- else
- raise_application_error(-20001, 'test FALSE');
- end if;
- EXCEPTION
- WHEN is_used_true THEN
- DBMS_OUTPUT.put_line('IT IS TRUE ERROR');
- when is_used_false then
- DBMS_OUTPUT.put_line(sqlcode);
- DBMS_OUTPUT.put_line('it is false error');
- end;
- /
--在两种场合下,建议使用EXCEPTION_INIT
--为一些经常用到的,匿名的系统异常命名
--为使用RAISE_APPLICATION_ERROR抛出的应用专有错误命名
- declare
- v_bool boolean := false;
- begin
- if v_bool then
- raise_application_error(myerrortype.n_true_error, 'test true');
- else
- raise_application_error(myerrortype.n_false_error, 'test FALSE');
- end if;
- EXCEPTION
- WHEN myerrortype.is_used_true THEN
- DBMS_OUTPUT.put_line('IT IS TRUE ERROR');
- when myerrortype.is_used_false then
- DBMS_OUTPUT.put_line('it is false error');
- end;
- /
- create or replace package myerrortype is
- is_used_true EXCEPTION;
- is_used_false EXCEPTION;
- n_true_error number := -20000;
- n_false_error number := -20001;
- pragma exception_init(is_used_true, -20000);
- pragma exception_init(is_used_false, -20001);
- end myerrortype;
- /
---被命名的系统异常
--内置包standard,最常用的命名异常可以在这个包中找到,缺省包,使用其中异常不需要加上包名,比如
when standard.no_data_found then
when not_data_found then
--也有其它预定义的异常在其它内置包中,比如DBMS_LOB包,不是缺省包,使用时需要加上包名
DBMS_LOB.opt_deduplicate
--常用的异常
--ZERO_DIVIDE ora-01476 除零错误
--VALUE_ERROR ORA-06502 plsql在做类型转换、数值截断、或者数值、字符数据的无效约束
--transaction_backed_out ora-00061一个事务的远程部分被回滚
--too_many_values ora-01422 select into 返回了多行记录
--not_data_found ora-01403 sqlcode = 100
--异常的作用范围
--被命名的系统异常:全局可用
--被命名的程序定义的异常:只能在程序块中使用,如果是包中定义的异常,有包的EXECUTE权限都可以使用
--匿名系统异常:在任何程序块中,使用WHEN OTHERS THEN 部分处理
--匿名的自定义异常:调用RAISE_APPLICATION_ERROR是定义,并调用
- CREATE OR REPLACE PROCEDURE TEST_EXCEPTION
- IS
- IS_TEST_EXCEPTION EXCEPTION ;
- BEGIN
- RAISE IS_TEST_EXCEPTION ;
- END ;
- /
- declare
- IS_TEST_EXCEPTION EXCEPTION ;
- BEGIN
- DECLARE
- IS_TEST_EXCEPTION EXCEPTION ;
- BEGIN
- RAISE IS_TEST_EXCEPTION;
- END ;
- EXCEPTION
- WHEN IS_TEST_EXCEPTION THEN
- DBMS_OUTPUT.put_line('自定义异常');
- END;
- /
- --抛出异常
- --数据库检测到错误时,可以抛出异常
- --使用RAISE语句抛出异常
- --使用内置的RAISE_APPLICATION_ERROR过程抛出异常
- --RAISE语句
- raise exception_name;--可以抛出当前块自定义的异常,也可以是系统定义异常
- raise pkg_name.exception_name; --包中声明的异常
- raise; --不需要异常名称,只能异常处理单元 WHEN 语句中使用,传播异常,在异常单元再次抛出同一个异常
- DECLARE
- is_used_true EXCEPTION;
- v_bool boolean := false;
- BEGIN
- BEGIN
- IF not v_bool THEN
- RAISE is_used_true;
- END IF;
- EXCEPTION
- WHEN is_used_true THEN
- DBMS_OUTPUT.put_line('再次抛出is_used_true');
- raise;
- END;
- exception
- when is_used_true then
- DBMS_OUTPUT.put_line('捕获到内层抛出的异常is_used_true');
- END;
- /
--raise_application_error,相较于raise,可以给异常加上一段错误信息
--执行这个过程,当前PLSQL块的执行会被终止,对out或者in out参数(没有使用NOCOPY)所做的修改会被撤销
--但对于全局数据结构的修改,比如包变量,数据库对象,不会回滚,必须使用rollback
- create or replace procedure test_error is
- begin
- update ma_users t set t.user_point = 0 where t.user_name = '乱世佳人';
- RAISE_APPLICATION_ERROR(-20008, '更新错误');
- end;
- /
- begin
- test_error;
- exception
- when others then
- ROLLBACK;
- end;
- /
--处理异常
--一旦有异常抛出,当前PL/SQL块就会终止正常执行,把控制传递给异常处理单元,这个异常或者
--被当前PL/SQL块中的处理句柄处理或者抛给外层块
- DECLARE
- BEGIN
- [EXCEPTION
- --EXCEPTION HANDLERS
- ]
- END ;
- --异常句柄语法
- WHEN EXCEPTION_NAME [OR EXCEPTION_NAME] THEN
- EXECUTABLE STATEMENTS
- --或者
- WHEN OTHERS THEN
- EXECUTABLE STATEMENTS;
- --一个异常处理单元可以有多个异常句柄,在结构上类似条件CASE语句
- exception
- when no_data_found then
- --doing something
- when ... then
- -- doing something
- when others then
- -- doing something
- end ;
--内置的错误函数
--SQLCODE:返回代码中最后一次抛出的错误代码。如果没有任何错误,则返回0;
--SQLERRM:返回某个错误代码对应的错误信息。如果没有给SQLERRM传递错误代码,就会返回SQLCODE的错误代码--累积信息
--对应的消息,最大长度512个字节
- DECLARE
- v_num number(11, 10) := 2;
- BEGIN
- dbms_output.put_line('sqlcode1:=' || sqlcode);
- dbms_output.put_line('sqlerrm:=' || sqlerrm);
- dbms_output.put_line('sqlerrm1:=' || sqlerrm(-1476));
- begin
- v_num := 35;
- exception
- when others then
- dbms_output.put_line('sqlcode2:=' || sqlcode);
- dbms_output.put_line('sqlerrm2:=' || sqlerrm);
- v_num := v_num / 0;
- end;
- exception
- when others then
- dbms_output.put_line('sqlcode3:=' || sqlcode);
- dbms_output.put_line('sqlerrm3:=' || sqlerrm);
- END;
- /
- -- 其它函数
- DECLARE
- v_num number(11, 10) := 2;
- BEGIN
- begin
- v_num := 35;
- exception
- when others then
- dbms_output.put_line('errorline:=' ||
- dbms_utility.format_error_backtrace);
- --dbms_output.put_line('errorline:=' ||dbms_utility.format_call_stack);
- v_num := v_num / 0;
- end;
- exception
- when others then
- dbms_output.put_line('errorline:=' ||
- dbms_utility.format_error_backtrace);
- END;
- /
--能够定位到异常的行数,想一想就激动人心
--在一个单独的句柄中包含多个异常
--未处理的异常会怎样?
--在外层块或者个程序
--捕获任何可能传播过来的异常
--为错误记录日志,从而开发人员能够分析是什么造成了这个问题
--返回一个状态码,描述或其他信息,以帮助宿主环境决定采取适当的措施
- --异常的传播
- DECLARE
- v_num number(11, 10) := 2;
- BEGIN
- begin
- v_num := 35;
- exception
- when no_data_found then
- dbms_output.put_line('no data found '||sqlerrm);
- WHEN OTHERS THEN
- dbms_output.put_line('TEST2'||sqlerrm);
- end;
- dbms_output.put_line('DOING SOMETHING');
- exception
- when others then
- dbms_output.put_line(sqlerrm);
- END;
- /
- -- when others 的使用
- --构建一个有效的错误管理架构
- --确定异常管理策略
- --是否要在每一个PL/SQL中都包含一个异常处理单元
- --是否应该只在最外层或者最顶层块中包含一个异常处理单元
- --当错误发生时,如何管理事务?
- --对不同类的异常标准话处理:deliberate,unfortunate,unexpected
- --组织好对应用专有错误代码的使用,使用配置表配置?
- --使用标准化的错误管理程序
- --创建通用错误处理的标准模板
----------------------------------------------------------------------------------------------------------------------
1. 最近招人做了些面试,凡是简历上写熟练掌握PL/SQL,我都会问在PL/SQL中怎么捕获异常,怎么抛出异常,
怎么获取异常信息或发生异常的行号?这些问题作为第一道作业。大家学完异常处理这节课后,用自己的语言描述下即可。
1:对于常见的oracle预定于的异常,no_data_found和too_many_rows是经常发生的异常,对于plsql要捕获
2:对于预定于的异常,当发生异常的时候,会自动根据名字与oracle内部定义的sqlcode(异常编号)和 sqlerrm(异常消息)关联。
3:如果将有异常代码区的块不捕获异常,则自动传播到外层块。
4:用户自定义的异常,需要声明,显式通过raise,raise_application_error抛出
5:WHEN OTHERS总是放在最后
2. 构建一个好的系统,最好有一个通用的记录异常方法以简化代码。还是之前的订购网站,
请大家设计一个异常日志表,可以记录异常发生时的时间,操作人,方法名,发生异常时的行号,
异常信息,错误信息级别(比如提示信息,错误,重要等)。并且设计一个通用的方法供发生异常时调用。
- --异常日志表
- create table exception_logs
- (created_on date default sysdate,
- created_by varchar2(40) default 'system',
- option_users varchar2(32),
- method_name varchar2(32),
- exception_time date,
- exception_line varchar2(32),
- exception_code number,
- exception_message varchar2(500),
- exception_level varchar2(32));
- create table exception_level
- (
- created_on date default sysdate,
- created_by varchar2(40) default 'system',
- exception_level number,
- exception_type varchar2(20),
- exception_code number);
- --exception_level
- --dbms_utility.format_error_backtrace/SQLERRM/sqlcode
- --0 未定义
- --1 提示信息(数据不存在 no_data_found ora-01403/ORA-01403/100)
- --2 错误信息 (返回多行数据 too_many_rows ora-01422/ORA-01422,除数为0 ora-01476/ORA-01476 ,数字或值错误 ora-06502/ORA-06502,违反唯一约束条件 ora-65512/ORA-00001)
- --3 重要信息 ora-0600
- create index idx_exception_level on exception_level (exception_code)
- insert into exception_level
- (exception_level,exception_type,exception_code)
- values
- (1,'deliberate',100);
- insert into exception_level
- (exception_level,exception_type,exception_code)
- values
- (2,'unfortunate',-1422);
- insert into exception_level
- (exception_level,exception_type,exception_code)
- values
- (2,'unfortunate',-1476);
- insert into exception_level
- (exception_level,exception_type,exception_code)
- values
- (2,'unfortunate',-6502);
- insert into exception_level
- (exception_level,exception_type,exception_code)
- values
- (2,'unfortunate',-1);
- insert into exception_level
- (exception_level,exception_type,exception_code)
- values
- (3,'unexpected',-600);
- commit;
- --package exception
- CREATE OR REPLACE PACKAGE SCOTT.exception_logs_pkg
- IS
- PROCEDURE exception_logs_p (
- i_option_users IN exception_logs.option_users%TYPE,
- i_method_name IN exception_logs.method_name%TYPE,
- i_exception_line IN exception_logs.exception_line%TYPE,
- i_exception_code IN exception_logs.exception_code%TYPE,
- i_exception_message IN exception_logs.exception_message%TYPE--i_exception_level IN exception_logs.exception_level%TYPE
- );
- END exception_logs_pkg;
- /
- CREATE OR REPLACE PACKAGE BODY SCOTT.exception_logs_pkg
- IS
- /******************************************************************************
- NAME: exception_logs_pkg
- PURPOSE:
- REVISIONS:
- Ver Date Author Description
- --------- ---------- --------------- ------------------------------------
- 1.0 2016-03-08 hongquan 1. Created this package body.
- ******************************************************************************/
- PROCEDURE exception_logs_p (
- i_option_users IN exception_logs.option_users%TYPE,
- i_method_name IN exception_logs.method_name%TYPE,
- i_exception_line IN exception_logs.exception_line%TYPE,
- i_exception_code IN exception_logs.exception_code%TYPE,
- i_exception_message IN exception_logs.exception_message%TYPE--i_exception_level IN exception_logs.exception_level%TYPE
- )
- IS
- PRAGMA AUTONOMOUS_TRANSACTION;
- v_sysdate DATE DEFAULT SYSDATE;
- v_exception_level NUMBER DEFAULT 0;
- BEGIN
- BEGIN
- SELECT exception_level
- INTO v_exception_level
- FROM exception_level
- WHERE exception_code=i_exception_code;
- EXCEPTION
- WHEN OTHERS THEN
- v_exception_level:=3;
- END ;
- BEGIN
- INSERT INTO exception_logs (option_users,
- method_name,
- exception_time,
- exception_line,
- exception_code,
- exception_message,
- exception_level)
- VALUES (i_option_users,
- i_method_name,
- v_sysdate,
- i_exception_line,
- i_exception_code,
- i_exception_message,
- v_exception_level);
- COMMIT;
- EXCEPTION
- WHEN OTHERS
- THEN
- ROLLBACK;
- END;
- END;
- END exception_logs_pkg;
- /
- --调用
- DECLARE
- v_num number(11, 10) := 2;
- v_erroeline varchar2(100);--not a number
- v_sqlcode number;
- v_sqlerrm varchar2(100);
- v_sysdate date default sysdate;
- v_user varchar2(32) default user;
- BEGIN
- begin
- --v_num := 35;
- --v_sysdate := 35;
- --v_num:=1/0;
- select ename into v_num from emp where empno=7521;
- --insert into emp(empno) values (7499);
- exception
- when others then
- v_erroeline:=dbms_utility.format_error_backtrace;
- v_sqlcode:=sqlcode;
- v_sqlerrm:=substr(SQLERRM,1,100);
- dbms_output.put_line('v_erroeline=='||v_erroeline);
- dbms_output.put_line('v_sqlcode1=='||v_sqlcode);
- dbms_output.put_line('v_sqlerrm1=='||v_sqlerrm);
- dbms_output.put_line('errorline1:=' ||dbms_utility.format_error_backtrace);
- -- dbms_output.put_line('errorline2:=' ||dbms_utility.format_call_stack);
- exception_logs_pkg.exception_logs_p (v_user,'testerror',v_erroeline,v_sqlcode, v_sqlerrm);
- RAISE;
- end;
- exception
- when others then
- dbms_output.put_line('errorline3:=' ||dbms_utility.format_error_backtrace);
- --v_sqlcode:=sqlcode;
- --v_sqlerrm:=substr(SQLERRM,1,100);
- dbms_output.put_line('v_sqlcode2=='||v_sqlcode);
- dbms_output.put_line('v_sqlerrm2=='||v_sqlerrm);
- rollback;
- END;
- /
PL/SQL 训练03 --异常的更多相关文章
- pl/sql programming 03 语言基础
PL/SQL 块结构 最小的有意义的代码单元叫做 块(block). 一个块是一组代码, 这个块给出了执行边界, 也为变量声明和异常处理提供了作用范围, pl/sql 准许我们创建匿名块和命名块, 命 ...
- PL/SQL 训练13--plsql 优化
--数据缓存技术 --PGA和SGA---SGA:系统全局区域--PGA:Process Global Area是为每个连接到Oracle的用户进程保留的内存. ---PLSQL从PGA获取信息的速度 ...
- PL/SQL 训练12--动态sql和绑定变量
--什么是动态SQL?动态PL/SQL--动态SQL是指在运行时刻才构建执行的SQL语句--动态PL/SQL是指整个PL/SQL代码块都是动态构建,然后再编译执行 --动态SQL来可以用来干什么? - ...
- PL/SQL 训练11--包
--所谓包,就是把一组PL/SQL的代码元素组织在一个命名空间下.--一种可以把程序或者其他的PL/SQL元素比如游标.类型.变量的组织结构在一起的结构(包括逻辑结构和物理结构)--包提供了非常重要的 ...
- PL/SQL 训练10--io及文件操作
多数程序只需要通过SQL和底层数据库进行交互--有些情况,不可避免的还是会有一些场景,需要从PL/SQL给外部环境发送信息--或是从一些外部的源读入信息 --这节课介绍下面这些内置包 dbms_out ...
- PL/SQL 训练08--触发器
--什么是触发器呢?--一触即发,某个事件发生时,执行的程序块?--数据库触发器是一个当数据库发生某种事件时作为对这个事件的响应而执行的一个被命名的程序单元 --适合场景--对表的修改做验证--数据库 ...
- PL/SQL 训练05--游标
--隐式游标--通过一个简单的SELECT ...INTO 语句提取一行数据,并放在一个局部变量中,最简单获取数据的途径 --显示游标--可以在声明单元明确的声明一个查询,这样可以在一个或多个程序中打 ...
- PL/SQL 训练04--事务
--pl/sql通过SQL和ORACLE数据库紧密的整合在一起--在pl/sql中可以执行任何操作语句(DML语句),包括INSERT,UPDATE,DELETE,MERGE,也包括查询语句--可否执 ...
- PL/SQL 训练02--集合数组
1. 请列举关联数组.嵌套表.VARRAY三种集合类型的区别区别:1 关联数组只能在plsql中使用,嵌套表,varray可用于sql中,数据库表中的列2 嵌套表,varray必须在使用的时候初始化, ...
随机推荐
- java中的char类型所占空间
java中统一使用unicode编码,所以每个字符都是2个字节16位.unicode包括中文,所以对String类计算长度的时候,一个中文和一个英文都是一个长度.String voice = &quo ...
- Ajax-02 iframe实现伪“Ajax”
需求: 用户输入URL,使用iframe将目标URL的内容加载到页面指定位置(局部刷新) <!DOCTYPE html> <html lang="en"> ...
- Device Drivers Should Not Do Power Management
有人对现有的电源管理提出了意见,认为驱动程序不应该做电源管理,paper地址在这里: http://www.ruf.rice.edu/~mobile/publications/xu2014apsys. ...
- windows平台下安装Beautiful Soup
1.Beautiful Soup 4.3.2 下载 2.下载完成后解压,放在Python的安装目录下,假设放到C:/Python 3.运行cmd,切换到C:/Python/Beautiful Soup ...
- dga2
0e527eaf_5ec5_4623_9fe9_e459583acd72.com0fmgm1cuu7h1279dghgka0ltg.com0ydlanpuh4e2wl9h6udk6.com10uz8k ...
- inline,block,inline-block解析
display:block就是将元素显示为块级元素. block元素的特点是: 总是在新行上开始: 高度,行高以及顶和底边距都可控制: 宽度缺省是它的容器的100%,除非设定一个宽度 <div& ...
- 血的教训 password写成passward,教训应该从首页赋值 参数名
血的教训 password写成passward,教训应该从首页赋值 参数名
- 《深入理解java虚拟机》学习笔记之虚拟机即时编译详解
郑重声明:本片博客是学习<深入理解java虚拟机>一书所记录的笔记,内容基本为书中知识. Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块 ...
- vim 插件使用
a.vim的安装相当简单,下载a.vim后丢进Vim插件目录(一般为~/.vim/plugin),必要时再重启一下Vim就可以使用了. 头/源文件切换命令 :A 头文件/源文件切换 :AS 分割窗后并 ...
- struts2.5框架使用通配符指定方法,某一个匹配不到
在学习struts框架时经常会使用到通配符调用方法,如下:但奇怪的是,在validateName请求老报404,其他的都是ok的,开始以为是配置错了,检查好久才知道: <action name= ...