1.游标Cursor
游标,用于操作结果集,类似jdbc的ResultSet
1.1 语法
CURSOR 游标名 [(参数名,数据类型),(参数名,数据类型),…] is select 语句;
1.2 使用步骤
- 打开游标执行查询:open 游标名;
- 取一行游标值:fetch 游标名 into 变量名;
- 关闭游标:close 游标名;
- 游标的结束方式:exit when 游标名%notfound
1.3 例子
-- 输出所有员工的姓名
declare
cursor vrows is
select * from emp;
vrow emp%rowtype;
begin
open vrows;
loop
fetch vrows
into vrow;
exit when vrows%notfound;
dbms_output.put_line(vrow.ename);
end loop;
close vrows;
end;
-- 输出指定部门的员工姓名
declare
-- 声明游标
cursor vrows(vdeptno number) is select * from emp where deptno = vdeptno;
-- 记录型变量
vrow emp%rowtype;
begin
-- 1.打开游标
open vrows(20);
-- 提取数据
loop
fetch vrows into vrow;
exit when vrows%notfound;
dbms_output.put_line(vrow.ename);
end loop;
-- 关闭游标
close vrows;
end;
-- 扩展: 使用for循环遍历游标
declare
-- 声明游标
cursor vrows is select * from emp;
begin
for vrow in vrows loop
dbms_output.put_line('姓名:'||vrow.ename||' 工资:'||vrow.sal);
end loop;
end;
1.4 系统引用游标
变量名 sys_refcursor;
open 变量名 for select 语句;
-- 输出所有员工的姓名和工资
declare
-- 声明一个游标
vrows sys_refcursor;
-- 声明一个变量
vrow emp%rowtype;
begin
-- 1.打开游标
open vrows for select * from emp;
-- 提取数据
loop
fetch vrows into vrow;
exit when vrows%notfound;
dbms_output.put_line('姓名:'||vrow.ename||' 工资:'||vrow.sal);
end loop;
-- 关闭游标
close vrows;
end;
2. 异常
2.1 常见系统异常
- zero_divide : 除零异常
- value_error : 类型转换异常
- no_data_found: 空指针异常
- too_many_rows : 找到了多行记录,但是赋值给了一行
- others: 捕获所有异常
2.2 语法
-- 语法:
declare
-- 声明部分
begin
-- 编写业务
exception
--处理异常
when 异常1 then
--处理异常
when 异常2 then
--处理异常
when others then 捕获所有异常
--处理异常
end;
declare
i number;
vrow emp%rowtype;
begin
-- i := 5/0;
-- i := 'adsfadfwerwer';
-- select * into vrow from emp where empno=1234567;
select * into vrow from emp;
exception
when too_many_rows then
dbms_output.put_line('发生了找到了多行记录,但是赋值给了一行的例外');
when no_data_found then
dbms_output.put_line('发生了没有找到的例外');
when value_error then
dbms_output.put_line('发生了类型转换例外');
when zero_divide then
dbms_output.put_line('发生了除零例外');
when others then
dbms_output.put_line('发生了未知的例外');
end;
2.3 自定义异常
声明:
例外名称 exception;
抛出例外:
raise 例外名称
-- 自定义例外
declare
-- 声明例外
no_emp_found exception;
begin
-- 抛出例外
raise no_emp_found;
exception
when no_emp_found then
dbms_output.put_line('发生了no_emp_found的例外');
when others then
dbms_output.put_line('发生了未知的例外');
end;
-- 查询指定编号的员工信息,如果没有找到则抛出自定义的例外
-- 如果没有找到员工,抛出的是自定义例外
-- 错误的演示
declare
-- 声明例外
no_emp_found exception;
-- 声明记录型变量
vrow emp%rowtype;
begin
select * into vrow from emp where empno=9999; -- 已经抛出了例外 no_data_found
if vrow.ename is null then
raise no_emp_found;
end if;
exception
when no_emp_found then
dbms_output.put_line('没有找到员工,抛出了自定义的例外');
when others then
dbms_output.put_line('发生了未知的');
end;
-- 如果没有找到员工,抛出的是自定义例外
-- 游标 : 如果没有找到数据 %notfound
declare
-- 声明一个游标
cursor vrows is select * from emp where empno=1234567;
-- 自定义例外
no_emp_found exception;
-- 声明一个记录型变量
vrow emp%rowtype;
begin
-- 1,打开游标
open vrows;
-- 将游标向下移动
fetch vrows into vrow;
if vrows%notfound then
raise no_emp_found;
end if;
-- 关闭游标
close vrows;
exception
when no_emp_found then
dbms_output.put_line('没有找到员工');
when others then
dbms_output.put_line('未知的例外');
end;
3. 存储过程
存储过程 : 实际上是将一段已经编译好了的PLSQL代码片断,封装在数据库中,方便调用。
作用:
1. 方便代码重用
2. 提高SQL的执行的效率
语法:
create [or replace] procedure 存储过程名称(参数1 in|out 参数类型,参数2 in|out 参数类型)
is|as
-- 声明部分
begin
-- 业务逻辑部分
end;
in : 输入参数
out : 输出参数
-- 给指定员工涨薪,并打印涨薪前和涨薪后的工资
-- 员工编号 , 输入参数
-- 涨多少
create or replace procedure proc_updatesal(vempno in number,vcount in number)
is
vsal number;
begin
-- 查询涨薪前的工资
select sal into vsal from emp where empno=vempno;
-- 打印工资
dbms_output.put_line('涨薪前:'||vsal);
-- 涨薪
update emp set sal = vsal+vcount where empno=vempno;
-- 打印涨薪后
dbms_output.put_line('涨薪后:'||(vsal+vcount));
-- 提交事务
commit;
end;
-- 调用方式1
call proc_updatesal(7369,10);
-- 调用方式2
declare
begin
proc_updatesal(7369,-100);
end;
--存储过程: 获取指定员工的年薪
-- 参数 : 员工编号 in number
-- 参数: 年薪 out number
create or replace procedure proc_getyearsal(vempno in number,vyearsal out number)
is
begin
select sal*12+nvl(comm,0) into vyearsal from emp where empno=vempno;
end;
-- 调用
declare
yearsal number;
begin
proc_getyearsal(7369,yearsal);
dbms_output.put_line('年薪:'||yearsal);
end;
4. 函数
存储函数:
实际上是将一段PLSQL代码片断,封装在数据库,方便别人去调用
作用:
1.提高代码复用性
2.提高执行效率
语法:
create [or replace] function 函数名称(参数1 in|out 参数类型) return 返回类型
is|as
begin
end;
区别:
1.函数有返回值,过程没有
2.过程能实现的,函数能实现
3.函数能实现的,过程也能实现
4.函数可以直接在SQL语句中使用
5.函数和过程本质上没有区别
-- 获取指定员工的年薪
create or replace function func_getyearsal(vempno number) return number
is
--声明一个变量
vyearsal number;
begin
select sal*12+nvl(comm,0) into vyearsal from emp where empno=vempno;
return vyearsal;
end;
-- 调用函数
declare
yearsal number;
begin
yearsal := func_getyearsal(7369);
dbms_output.put_line('年薪:'||yearsal);
end;
-- 在SQL语句中直接调用
select emp.*,func_getyearsal(emp.empno) "年薪" from emp;
select * from emp;
5. 触发器
触发器:
当我们在SQL中执行了insert | update | delete这些操作的时候,可以去触发一段PLSQL代码片断执行
语法:
create [or replace] trigger 触发器名称
before | after
insert | update | delete
on 表名
[for each row] //行级触发器,写这个语句
declare
-- 声明
begin
-- 业务
end;
作用: 表的内容发生变化
1. 当数据发生变化的时候, 需要触发一段业务逻辑的执行
2. 数据校验
触发器的分类:
语句级触发器:
一条语句,不管影响多少行记录,都只触发一次
行级触发器:
一条语句,影响了多少行记录,就触发多少次
:new 新的记录
:old 旧的记录
-- 当向emp表插入数据之前,就输出一句hello trigger
create or replace trigger tri_test1
before
insert on emp
declare
begin
dbms_output.put_line('hello trigger');
end;
insert into emp(empno,ename) values(9527,'huaan');
update emp set sal=10 where empno=9527;
-- 校验指定周六不能插入员工
--before insert
create or replace trigger tri_check
before
insert on emp
declare
vday varchar2(20);
begin
-- 查询今天周几
select trim(to_char(sysdate,'day')) into vday from dual;
-- 条件判断
if vday='saturday' then
raise_application_error(-20000,'老板周六不在,不能插入数据!');
end if;
end;
create or replace trigger tri_test2
before
update on emp
declare
begin
dbms_output.put_line('语句级触发器');
end;
create or replace trigger tri_test3
before
update on emp
for each row
declare
begin
-- :new sal+10 :old sal
dbms_output.put_line('行级触发器:'||:new.sal||' 旧的记录:'||:old.sal);
end;
update emp set sal=sal-10;
/*
模拟mysql中的ID自动增长 auto_increment
insert into 表名 values(null,zhangsan);
触发器 : before insert
序列 : 1,2,3,4,5,6,7,8...
*/
create table ppp(
pid number primary key,
pname varchar2(20)
);
insert into ppp values(null,'zs');
-- 创建一个序列
create sequence seq_ppp_index;
-- 触发器
create or replace trigger tri_ppp
before
insert on ppp
for each row
declare
begin
if :new.pid is null then
select seq_ppp_index.nextval into :new.pid from dual;
end if;
end;
insert into ppp values(null,'zs');