PL/SQL 训练03 --异常

时间:2022-10-01 11:14:54

--程序员在开发的时候,经常天真的认为这个世界是完美的,用户如同自己般聪明,总能按照自己设想的方式
--操作系统输入数据。但残酷的事实告诉我们,这是不可能的事情,用户总会跟我们相反的方式操作系统
--于是,无数的问题砸向我们。
--那么怎么办呢?
---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 --异常的更多相关文章

  1. pl/sql programming 03 语言基础

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

  2. PL/SQL 训练13--plsql 优化

    --数据缓存技术 --PGA和SGA---SGA:系统全局区域--PGA:Process Global Area是为每个连接到Oracle的用户进程保留的内存. ---PLSQL从PGA获取信息的速度 ...

  3. PL/SQL 训练12--动态sql和绑定变量

    --什么是动态SQL?动态PL/SQL--动态SQL是指在运行时刻才构建执行的SQL语句--动态PL/SQL是指整个PL/SQL代码块都是动态构建,然后再编译执行 --动态SQL来可以用来干什么? - ...

  4. PL/SQL 训练11--包

    --所谓包,就是把一组PL/SQL的代码元素组织在一个命名空间下.--一种可以把程序或者其他的PL/SQL元素比如游标.类型.变量的组织结构在一起的结构(包括逻辑结构和物理结构)--包提供了非常重要的 ...

  5. PL/SQL 训练10--io及文件操作

    多数程序只需要通过SQL和底层数据库进行交互--有些情况,不可避免的还是会有一些场景,需要从PL/SQL给外部环境发送信息--或是从一些外部的源读入信息 --这节课介绍下面这些内置包 dbms_out ...

  6. PL/SQL 训练08--触发器

    --什么是触发器呢?--一触即发,某个事件发生时,执行的程序块?--数据库触发器是一个当数据库发生某种事件时作为对这个事件的响应而执行的一个被命名的程序单元 --适合场景--对表的修改做验证--数据库 ...

  7. PL/SQL 训练05--游标

    --隐式游标--通过一个简单的SELECT ...INTO 语句提取一行数据,并放在一个局部变量中,最简单获取数据的途径 --显示游标--可以在声明单元明确的声明一个查询,这样可以在一个或多个程序中打 ...

  8. PL/SQL 训练04--事务

    --pl/sql通过SQL和ORACLE数据库紧密的整合在一起--在pl/sql中可以执行任何操作语句(DML语句),包括INSERT,UPDATE,DELETE,MERGE,也包括查询语句--可否执 ...

  9. PL/SQL 训练02--集合数组

    1. 请列举关联数组.嵌套表.VARRAY三种集合类型的区别区别:1 关联数组只能在plsql中使用,嵌套表,varray可用于sql中,数据库表中的列2 嵌套表,varray必须在使用的时候初始化, ...

随机推荐

  1. Android编译错误, Ignoring InnerClasses attribute for an anonymous inner class

    今天在做android项目时,加入第三方包,一编译就报错.错误如下:[2012-01-13 14:51:25 - xxx] Dx warning: Ignoring InnerClasses attr ...

  2. ATT 汇编语法

      在研华的pc104上使用看门狗要使用汇编.使用汇编来修改CMOS里面的参数.也就是内联汇编.linux下gcc只支持ATT汇编.所以这儿有必要将ATT语法学习学习.以后需要的时候翻出来温习温习. ...

  3. 移动前端开发的viewport总结整理

    1.通俗讲移动设备上的viewport就是设备的屏幕上能用来显示我们的网页的那块区域,但不是浏览器可视区域.一般来讲,移动设备上的viewport都要大于浏览器的可视区域.移动设备上的浏览器会把默认的 ...

  4. javascript写的ajax请求

    <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> &l ...

  5. 多版本VisualStudio导致的&period;net版本问题

      写在前面:本博文是在我现有知识状态下写的, 我现在是小白, 有错误欢迎指正. 以后假如接触到更合理的见解, 我一定会修正这篇博文的. 本文原是在我本地笔记中待着的, 写于2016/05/17. 下 ...

  6. python程序在命令行执行提示ModuleNotFoundError&colon; No module named &&num;39&semi;XXX&&num;39&semi; 解决方法

    在ide中执行python程序,都已经在默认的项目路径中,所以直接执行是没有问题的.但是在cmd中执行程序,所在路径是python的搜索路径,如果涉及到import引用就会报类似ImportError ...

  7. python在图片上写汉字

    1.python opencv的putText只能画英文上去 2.借鉴这个https://blog.csdn.net/dcrmg/article/details/79108491 使用pil 首先,你 ...

  8. Linux运维故障排查思路

    linux系统故障 网络问题 linux系统无响应 linux系统无法启动 linux系统故障处理思路 1.重视报错信息,一般情况下此提示基本定位了问题的所在 2.查阅日志文件,系统日志和应用日志 3 ...

  9. c基础:函数参数是 struct&lpar;结构&rpar;,传的是引用,还是值?

    比如函数形式:void func(struct a data1, struct b data2); 答案: 只要不是指针或者数组都是传值,其实指针也是传递的地址值. 追问但是如果这个结构体里面有数组这 ...

  10. Linux command automake

    Linux command automake [Purpose]        Learning linux command automake for generate Makefile.in for ...