游标
游标(光标): 是用来操作查询结果集,相当于是JDBC中ResultSet
语法: cursor 游标名[(参数名 参数类型)] is 查询结果集
开发步骤:
1. 声明游标 cursor vrows is
2. 打开游标 open 游标名
3. 从游标中取数据 fetch 游标名 into 变量
游标名%found :找到数据
游标名%notfound : 没有找到数据
4. 关闭游标 close 游标名
系统引用游标
1. 声明游标 : 游标名 sys_refcursor
2. 打开游标: open 游标名 for 结果集
3. 从游标中取数据
4. 关闭游标
for循环遍历游标:
不需要声明额外变量
不需要打开游标
不需要关闭游标
不带参数游标
--输出员工表中所有的员工姓名和工资(不带参数游标)
游标:所有员工
声明一个变量,用来记录一行数据 %rowtype
*/
declare
--声明游标(范围)
cursor vrows is select * from emp;
--声明变量,记录一行数据
vrow emp%rowtype;
begin
--1.打开游标
open vrows;
--2.从游标提取数据
--循环取数据
loop
fetch vrows into vrow; --循环从游标中取数据到变量中
exit when vrows%notfound; --未取到数据时,退出循环
dbms_output.put_line('姓名:'||vrow.ename ||' 工资: ' || vrow.sal);
end loop;
--3.关闭游标
close vrows;
end;
带参数游标
--输出指定部门下的员工姓名和工资
/*
游标: 指定部门的所有员工
声明一个变量记录一行数据
*/
declare
--声明游标
cursor vrows(dno number) is select * from emp where deptno = dno; --select * from emp where deptno = dno 取出deptno(部门)编号为dno的所有员工
--声明变量
vrow emp%rowtype;
begin
--1.打开游标 , 指定10号部门
open vrows(10);
--2. 循环遍历,取数据
loop
fetch vrows into vrow;
exit when vrows%notfound;
dbms_output.put_line('姓名:'||vrow.ename ||' 工资: ' || vrow.sal);
end loop;
close vrows;
end;
系统引用游标
--输出员工表中所有的员工姓名和工资
declare
--声明系统引用游标
vrows sys_refcursor;
--声明一个变量
vrow emp%rowtype;
begin
--1.打开游标
open vrows for select * from emp;
--2.取数据
loop
fetch vrows into vrow;
exit when vrows%notfound;
dbms_output.put_line('姓名:'||vrow.ename ||' 工资: ' || vrow.sal);
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 || '工作:'|| vrow.job);
end loop;
end;
例外
例外:(意外)程序运行的过程发生异常,相当于是JAVA中的异常
declare
--声明变量
begin
--业务逻辑
exception
--处理异常
when 异常1 then
...
when 异常2 then
...
when others then
...处理其它异常
end;
zero_divide : 除零异常
value_error : 类型转换异常
too_many_rows : 查询出多行记录,但是赋值给了rowtype记录一行数据变量
no_data_found : 没有找到数据
自定义异常:
异常名 exception; 需要声明异常名
raise 异常名
declare
vi number;
vrow emp%rowtype;
begin
--vi := 8/0;
--vi := 'aaa';
--select * into vrow from emp;
select * into vrow from emp where empno=1234567;
exception
when zero_divide then
dbms_output.put_line('发生了除零异常');
when value_error then
dbms_output.put_line('发生了类型转换异常');
when too_many_rows then
dbms_output.put_line(' 查询出多行记录,但是赋值给了rowtype记录一行数据变量');
when no_data_found then
dbms_output.put_line('没有找到数据异常');
when others then
dbms_output.put_line('发生了其它异常' || sqlerrm);
end;
存储过程
存储过程: 实际上是封装在服务器上一段PLSQL代码片断,已经编译好了的代码
1.客户端取调用存储过程,执行效率就会非常高效
语法:
create [or replace] procedure 存储过程的名称(参数名 in|out 参数类型,参数名 in|out 参数类型)
is | as
--声明部分
begin
--业务逻辑
end;
调用存储过程的两种方式
--方式1
call proc_updatesal();
--方式2
declare
begin
proc_updatesal();
end;
--给指定员工涨薪,并打印涨薪前和涨薪后的工资
参数 : in 员工编号
参数 : in 涨多少
声明一个变量 : 存储涨工资前的工资
查询出当前是多少
打印涨薪前的工资
更新工资
打印涨薪后的工资
create or replace procedure proc_updatesal(vempno in number,vnum 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 + vnum where empno = vempno;
--输出涨薪后的工资
dbms_output.put_line('涨薪后:'||(vsal+vnum));
--提交
commit;
end;
存储函数
存储函数: 实际上是一段封装是Oracle服务器中的一段PLSQL代码片断,它是已经编译好了的代码片段
语法:
create [or replace] function 存储函数的名称(参数名 in|out 参数类型,参数名 in|out 参数类型) return 参数类型
is | as
begin
end;
存储过程和函数的区别:
1.它们本质上没有区别
2.函数存在的意义是给过程调用 存储过程里面调用存储函数
3.函数可以在sql语句里面直接调用
4.存储过程能实现的,存储函数也能实现,存储函数能实现的,过程也能实现
--查询指定员工的年薪
参数 : 员工的编号
返回 : 年薪
create or replace function func_getsal(vempno number) return number
is
--声明变量.保存年薪
vtotalsal number;
begin
select sal*12 + nvl(comm,0) into vtotalsal from emp where empno = vempno;
return vtotalsal;
end;
--调用存储函数
declare
vsal number;
begin
vsal := func_getsal(7788);
dbms_output.put_line(vsal);
end;
触发器
触发器: 当用户执行了 insert | update | delete 这些操作之后, 可以触发一系列其它的动作/业务逻辑
作用 :
在动作执行之前或者之后,触发业务处理逻辑
插入数据,做一些校验
语法:
create [or replace] trigger 触发器的名称
before | after
insert | update | delete
on 表名
[for each row]
declare
begin
end;
触发器的分类:
语句级触发器: 不管影响多少行, 都只会执行一次
行级触发器: 影响多少行,就触发多少次
:old 代表旧的记录, 更新前的记录
:new 代表的是新的记录
--新员工入职之后,输出一句话: 欢迎加入黑马程序员
create or replace trigger tri_test1
after
insert
on emp
declare
begin
dbms_output.put_line('欢迎加入黑马程序员');
end;
JAVA调用存储过程
JDBC的开发步骤:
1.导入驱动包
2.注册驱动
3.获取连接
4.获取执行SQL的statement
5.封装参数
6.执行SQL
7.获取结果
8.释放资源
public class TestProcedure {
@Test
/*
create or replace procedure proc_gettotalsal(vempno in number,vtotalsal out number)
is
begin
select sal*12 + nvl(comm,0) into vtotalsal from emp where empno = vempno;
end;
* */
public void test1() throws Exception{
//注册驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//2.获取连接
String url = "jdbc:oracle:thin:@192.168.80.100:1521:orcl";
String username = "dakang";
String password = "dakang";
Connection conn = DriverManager.getConnection(url, username, password);
//3.获取执行SQL的statement.这是一个固定格式 proc_gettotalsal是一个存储函数
String sql = "{call proc_gettotalsal(?,?)}";
CallableStatement state = conn.prepareCall(sql);
//设置输入参数
state.setInt(1, 7788);//设置员工编号
//注册输出参数类型
state.registerOutParameter(2, OracleTypes.NUMBER);
//4.执行statement
state.execute();
//5.获取执行结果
int totalsal = state.getInt(2);
//输出结果
System.out.println("工资:"+ totalsal);
//6.释放资源
state.close();
conn.close();
}
//调用存储函数
/*
create or replace function func_getsal(vempno number) return number
is
--声明变量.保存年薪
vtotalsal number;
begin
select sal*12 + nvl(comm,0) into vtotalsal from emp where empno = vempno;
return vtotalsal;
end;
*/
@Test
public void test2() throws Exception{
//注册驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//2.获取连接
String url = "jdbc:oracle:thin:@192.168.80.100:1521:orcl";
String username = "dakang";
String password = "dakang";
Connection conn = DriverManager.getConnection(url, username,password);
//3.获取执行SQL的statement
String sql = " {?= call func_getsal(?)}";
CallableStatement state = conn.prepareCall(sql);
//4.封装参数
//注册返回类型参数
state.registerOutParameter(1, OracleTypes.NUMBER);
//设置第二个参数
state.setInt(2, 7788);
//5.执行SQL
state.execute();
//6.获取结果
int totalsal = state.getInt(1);
System.out.println("年薪 : ====" +totalsal);
//7.释放资源
state.close();
conn.close();
}
}