linuxsir首页 LinuxSir.Org | Linux、BSD、Solaris、Unix | 开源传万世,因有我参与欢迎您!
网站首页 | 设为首页 | 加入收藏
您所在的位置:主页 > Linux数据库 >

Oracle SQL 异常处理

时间:2019-02-16  来源:未知  作者:admin666

今天学了异常处理

有预定义异常 与 用户自定义异常 还有 raise_application_error()函数
raise_application_error() 只能把异常抛出而不能处理异常。

预定义异常包括
NO_DATA_FOUND --没有找到数据
TOO_MANY_ROWS --找到多行数据
INVALID_CURSOR --失效的游标
ZERO_DIVIDE --除数为零
DUP_VAL_ON_INDEX 唯一索引中插入了重复值

预定义异常的示例:

declare
 v_id emp.empno%type; --声明变量
begin
 select empno into v_id from emp where deptno =40;
exception --异常处理
 when no_data_found then --no_data_found 是使用 select 某字段,然后 into 的时候,该字段没有出。
 rollback;
 dbms_output.put_line('没有40号部门记录');
 when too_many_rows then --too_many_rows 是使用 select 某字段,然后 into 的时候,该字段有多个值。
 rollback;
 dbms_output.put_line('返回多条记录');
 when others then --其它的异常出现
 rollback;
 dbms_output.put_line('出现其他错误.');
end; 

用户自定义异常就是用户定义一个异常情况,遇到这种情况再对这种情况进行处理
因为用户定义的异常不一定是Oracle返回的系统错误,系统不会自动触发,需要在声明部分定义。用户定义的异常处理部分基本上和预定义异常相同。

declare
 salary_level varchar2(1);
 invalid_salary_level exception; --声明异常
begin
 salary_level := 'D';
 if salary_level not in ('A','B','C') then
 raise invalid_salary_level; --触发异常
 end if;
exception --异常处理
 when invalid_salary_level then
 dbms_output.put_line('invalid salary level');
end; 

raise_application_error() 函数只是将异常抛出,不进行异常处理,并且终止程序。而用户自定义异常以及预定义异常不回终止程序,但会终止该 PL/SQL 代码块,所以一个存储过程中可以有多个 PL/SQL 代码块。

关于异常的语法及定义:

什么是异常:

PL/SQL用异常和异常处理器来实现错误处理
Oracle中出现错误的情形通常分为编译时错误(compile-time error)和运行时错误(run-time error)。
异常在PL/SQL执行过程中很可能出现
对异常如果不进行处理,异常可能会中断程序的运行

捕获异常的规则:

在异常部分WHEN 子句没有数量限制
当异常抛出后,控制无条件转到异常处理部分
EXCEPTION 关键词开始异常处理部分 WHEN OTHERS 为最后的一条子句
在异常块中,只有一个句柄会处理异常

关于异常捕获的函数:

SQLCODE 返回错误代码
SQLERRM 返回与错误代码关联的消息

保存任何非预期的异常的错误编码和错误消息

declare
 v_error_code NUMBER;
 v_error_message VARCHAR2(255);
BEGIN
EXCEPTION
 WHEN OTHERS THEN
 ROLLBACK;
 v_error_code := SQLCODE;
 v_error_message := SQLERRM;
 INSERT INTO err_logs VALUES (v_error_code, v_error_message);
END;

异常的传播

PL/SQL中错误处理的步骤:
步骤1:如果当前块中有该异常的处理器,则执行该异常处理语句块,然后控制权传递到外层语句块 步骤2:如果没有当前异常的处理器,把该异常传播给外层块。然后在外层执行步骤1:如果此语句在最外层语句块,则该异常将被传播给调用环境
没有处理的异常将沿检测异常调用程序传播到外面,当异常被处理并解决或到达程序最外层传播停止。异常是自里向外逐级传递的。

小题:

1.根据员工号,获得员工到目前为止参加工作年限(保留到整数),员工号不存在时提示 此员工号不存在 。

create or replace function get_workyear
 (v_id in emp.empno%type)
 return varchar2
 v_workyear integer;
BEGIN
 select to_char(sysdate,'yyyy')-to_char(hiredate,'yyyy') --两个数字字符串相减的值存到整数型变量中
 into v_workyear 
 from emp
 where emp.empno = v_id;
 return v_workyear;
EXCEPTION
 when no_data_found then
 dbms_output.put_line('此员工号不存在');
 return -1;
END get_workyear;

2.

①建表myemp。该表内容与emp一致;
②建存储过程。存储过程要的参数,和表里的字段一一对应。比如,表里有empno,存储过程就要有一个参数对应这字段i_empno,类型肯定和empno一样,如果你知道类型是number(4),就直接写成(i_empno in number(4),...)以此类推.
③功能实现,根据empno判断,如果myemp表里已经有这个empno,你就根据你传入的信息把empno的信息更新了,如果没有,就把你这些传入的字段,插入到表里,
eg:我只用两个字段来说明:empno、sal
入参1:123,1000,经过??断,myemp表里没有123这个empno,那么执行完存储过程,这个信息要插入到表里;
入参2:7369,2000,经判断,表里已经有这个编号,但sal为800,那么执行完存储过程,7369的sal更新为2000;
create or replace procedure store_info
 (v_empno in myemp.empno%type,
 v_ename in myemp.ename%type,
 v_job in myemp.job%type,
 v_mgr in myemp.mgr%type,
 v_hiredate in myemp.hiredate%type,
 v_sal in myemp.sal%type,
 v_comm in myemp.comm%type,
 v_deptno in myemp.deptno%type
 v_id myemp.empno%type:=0;
BEGIN
 select count(*) into v_id 
 from myemp 
 where myemp.empno = v_empno;
 if (v_id=0) then 
 insert into myemp(empno, ename, job, mgr, hiredate, sal, comm, deptno)
 values (v_empno,v_ename,v_job,v_mgr,v_hiredate,v_sal,v_comm,v_deptno); 
 else 
 update myemp
 set myemp.ename=nvl(v_ename,myemp.ename) , myemp.job=nvl(v_job,myemp.job),
 myemp.mgr=nvl(v_mgr,myemp.mgr) , myemp.hiredate=nvl(v_hiredate,myemp.hiredate),
 myemp.sal=nvl(v_sal,myemp.sal) , myemp.comm=nvl(v_comm,myemp.comm),
 myemp.deptno=nvl(v_deptno,myemp.deptno)
 where myemp.empno = v_empno ;
 end if;
END store_info;
begin 
 store_info(7369,null,null,null,null,2000,null,null);
end;

 结果

【注意】:
为什么要把这一题关于存储过程的题放到这里?
因为我起初用异常处理部分来写这一题......,这是不规范的。
本题中用到 count() 函数,count() 是用来计算满足条件的行数的,count(*) 计算所有的行,包括空值。

用异常处理来写本题的代码:

create table myemp(empno, ename, job, mgr, hiredate, sal, comm, deptno)
select empno, ename, job, mgr, hiredate, sal, comm, deptno
from emp;
create or replace procedure store_info
 (v_empno in myemp.empno%type,
 v_ename in myemp.ename%type,
 v_job in myemp.job%type,
 v_mgr in myemp.mgr%type,
 v_hiredate in myemp.hiredate%type,
 v_sal in myemp.sal%type,
 v_comm in myemp.comm%type,
 v_deptno in myemp.deptno%type
 v_id myemp.empno%type;
BEGIN
 select myemp.empno into v_id 
 from myemp 
 where myemp.empno = v_empno;
 update myemp
 set myemp.ename=v_ename, myemp.job=v_job,myemp.mgr=v_mgr,
 myemp.hiredate=v_hiredate, myemp.sal=v_sal,
 myemp.comm=v_comm,myemp.deptno=v_deptno
 where myemp.empno = v_id;
EXCEPTION
 when no_data_found then 
 insert into myemp(empno, ename, job, mgr, hiredate, sal, comm, deptno)
 values (v_empno,v_ename,v_job,v_mgr,v_hiredate,v_sal,v_comm,v_deptno);
END store_info;

3.
编写PL/SQL块,使用SELECT语句将管理者编号为空的员工的姓名及工作编号显示出来,如果符合条件的员工多于一人,则返回字符串 最高管理者人员过多! 字符串,如果找到没有符合条件的记录,则返回字符串 没有最高管理者,请指定
代码:

declare
 o_ename emp.ename%type;
 o_empno emp.empno%type;
 v_id emp.empno%type;
begin
 select emp.empno into v_id
 from emp
 where emp.mgr is null;
 select emp.ename into o_ename from emp where emp.empno = v_id;
 select emp.empno into o_empno from emp where emp.empno = v_id;
 dbms_output.put_line('员工姓名:'||o_ename||','|| '员工编号:'||o_empno);
exception
 when no_data_found then
 dbms_output.put_line('没有最高管理者,请指定');
 when too_many_rows then
 dbms_output.put_line('最高管理者人员过多');
end;

4.获得每个部门的平均工资,如果平均工资大于2000,视为用户定义的异常,提示 该部门的平均工资过高 。

declare
 cursor cemp 
 select dept.dname,avg(sal) 
 from emp,dept
 where emp.deptno = dept.deptno
 group by emp.deptno ,dept.dname;
 v_dname dept.dname%type ;
 v_asal emp.sal%type ;
 too_high_sal exception;
begin
 open cemp;
 loop --打开循环
 fetch cemp into v_dname,v_asal;
 exit when cemp%notfound;
 begin --这里写了一个 PL/SQL 代码块,里面可以做异常处理
 if v_asal 2000 then
 raise too_high_sal;
 end if;
 exception --异常处理,会终止此代码块。进入下一次循环
 when too_high_sal then
 dbms_output.put_line(v_dname||'该部门工资过高');
 end;
 end loop;
 close cemp;--注意end loop 与 close cemp 的先后顺序。必须是先结束循环,再关闭游标。
end;
友情链接
  • Mozilla发布Firefox 67.0.4,修复沙箱逃逸漏洞
  • 蚂蚁金服正式成为CNCF云原生计算基金会黄金会员
  • Firefox 68将采用Microsoft BITS安装更新
  • OpenSSH增加对存储在RAM中的私钥的保护
  • 谷歌想实现自己的curl,为什么?
  • Raspberry Pi 4发布:更快的CPU、更大的内存
  • Firefox的UA将移除CPU架构信息
  • Ubuntu放弃支持32位应用程序实属乌龙,Steam会否重回Ubuntu怀抱
  • Qt 5.13稳定版发布:引入glTF 2.0、改进Wayland以及支持Lottie动
  • 红帽企业Linux 7现已内置Redis 5最新版
  • Slack进入微软内部禁用服务清单,GitHub也在其列?
  • 安全的全新编程语言V发布首个可用版本
  • Windows Terminal已上架,快尝鲜
  • 阿里巴巴微服务开源生态报告No.1
  • 面世两年,Google地球将支持所有基于Chromium的浏览器
  • 推进企业容器化持续创新,Rancher ECIC千人盛典完美收官
  • CentOS 8.0最新构建状态公布,或于数周后发布
  • Debian移植RISC
  • 微软拆分操作系统的计划初现雏形
  • Oracle发布基于VS Code的开发者工具,轻松使用Oracle数据库
  • Ubuntu 19.10停止支持32位的x86架构
  • 微软为Windows Terminal推出全新logo
  • 联想ThinkPad P系列笔记本预装Ubuntu系统
  • 微软发布适用于Win7/8的Microsoft Edge预览版
  • 启智平台发布联邦学习开源数据协作项目OpenI纵横
  • 经过六个多月的延迟,微软终于推出Hyper
  • ZFS On Linux 0.8.1 发布,Python可移植性工作
  • DragonFly BSD 5.6.0 发布,HAMMER2状态良好
  • Linux Kernel 5.2
  • CentOS 8.0 看起来还需要几周的时间
  • 百度网盘Linux版正式发布
  • PCIe 6.0宣布:带宽翻倍 狂飙至256GB/s
  • PHP 7.4 Alpha 发布,FFI扩展,预加载Opcache以获得更好的性能
  • Canonical将在未来的Ubuntu版本中放弃对32位架构的支持
  • Scala 2.13 发布,改进的编译器性能
  • 微软的GitHub收购了Pull Panda,并且使所有订阅完全免费
  • Windows Subsystem for Linux 2 (WSL 2)现在适用于Windows 10用
  • Debian 10 “Buster”的RISC
  • MariaDB宣布发布MariaDB Enterprise Server 10.4
  • DXVK 1.2.2 发布,带来微小的CPU开销优化
  • DragonFlyBSD 5.6 RC1 发布,VM优化,默认为HAMMER2
  • PrimeNG 8.0.0 发布,支持Angular 8,FocusTrap等
  • GIMP 2.10.12 发布,一些有用的改进
  • 清华大学Anaconda 镜像服务即将恢复
  • Debian GNU/Linux 10 “Buster” 操作系统将于2019年7月6日发布
  • 时时彩论坛
  • 五星体育斯诺克
  • 北单比分直播
  • 河北11选5走势图
  • 福建体彩36选7开奖结果
  • 九龙图库下载