存储过程优点:减少数据库和服务器网络交互,提高执行效率(将写到java程序中的代码抽到数据库)
PLSQL不区分大小写,三个部分组成:
//声明部分:
DECLARE**
-- 声明变量、游标(没有可省略)
//可执行部分:
BEGIN
DBMS_output.put_line('hello world'); -- 打印语句
//异常部分:(没有的话可省略)
END;
1.2.1 普通变量
变量赋值的方式有两种:
1、直接赋值语句 := 比如 v_name :=‘zhangsan’
2、语句赋值,使用 select… into …赋值: (语法:select 值 into 变量)
CREATE OR REPLACE PROCEDURE TEST01 AS
-- 薪水
v_sal NUMBER ;
-- 地址
v_addr VARCHAR2(200);
-- 姓名
v_name varchar(200) :='zhangsan';
BEGIN
-- 直接赋值
v_sal := 1500;
-- 语句赋值
select '北京奥森' into v_addr from dual;
-- 打印输出
DBMS_OUTPUT.PUT_line( v_addr||v_name||v_sal ); // 北京奥森zhangsan1500
END TEST01;
注:1、创建新的存储过程直接在AS后面写变量,不需要写declare(可理解为declare优化成as)
2、需要我们先手动编译,在执行运行(与java语言不同,java语言是运行时自己完成编译)
1.2.2 引用型变量
好处:不需要考虑表中列的类型,使用%TYPE可以使得编码方式更灵活,更加适应数据库更新
通过 表名.列名%TYPE 指定变量的类型和长度,如 : v_name emp.ename%TYPE
create or replace PROCEDURE TEST01 AS
v_name a_i_app_use_time.org_name%TYPE;
v_id a_i_app_use_time.use_time_id%TYPE;
BEGIN
select org_name,use_time_id into v_name,v_id from a_i_app_use_time where use_time_id=517861;
-- 打印输出
DBMS_OUTPUT.PUT_line( v_name||v_id ); //合肥供电公司517861
END TEST01;
1.2.3 记录型变量
慎用!!!接受表中的一整行记录,相当于java中的一个对象
语法: 变量名称 表名%ROWTYPE , 例如: v_emp emp%rowtype;
create or replace PROCEDURE TEST01 AS
v_emp a_i_app_use_time%rowtype; // 格式:变量名称 表名%ROWTYPE
BEGIN
select * into v_emp from a_i_app_use_time where use_time_id=517861;
-- 打印输出 (需要哪个字段用 ‘对象.字段’ 的方式)
DBMS_OUTPUT.PUT_line(v_emp.org_no||v_emp.emp_no); //34401HF01
END TEST01;
使用场景:如果一个表有100个字段,使用引用变量声明会特别麻烦,这种情况可以使用记录型变量。
1.3.1 条件分支
语法:
BEGIN
IF 条件1 THEN 执行1
ELSIF 条件2 THEN 执行2 -- 注意关键字:ELSIF !!!!!!
ELSE 执行3
END IF;
END
//判断表中数据总条数,进行条件打印
CREATE OR REPLACE PROCEDURE STUDY AS
v_count NUMBER;
BEGIN
SELECT count(1) into v_count from a_i_app_use_time; //count(1)效率比count(*)高
if v_count>15 then
DBMS_OUTPUT.put_line('总条数为:'||v_count);
elsif v_count<=5 then // elsif 是关键字,不分开
DBMS_OUTPUT.put_line('0000:'||v_count);
else
DBMS_OUTPUT.put_line('1111:'||v_count);
end if; //过程执行语句,有开始就要有结束
END STUDY;
1.3.2 循环
CREATE OR REPLACE PROCEDURE STUDY AS
v_num number:=1; //开始的条件
BEGIN
loop
exit when v_num>5; //结束条件
v_num :=v_num+1; //循环条件
DBMS_OUTPUT.PUT_LINE(v_num); // 2、3、4、5、6
end loop; // 有开始就要有结束
END STUDY;
用于临时存储一个查询返回的多行数据(结果集,类似于java的jdbc连接返回的ResultSet集合),通过遍历游标,可以逐行访问处理该结果集的数据。
游标的使用方式:声明—>打开—>读取—>关闭
游标声明:
CURSOR 游标名[(参数列表)] IS 查询语句;
游标的打开:
OPEN 游标名;
游标的取值:
FETCH 游标名 INTO 变量列表;
游标的关闭:
CLOSE 游标名;
游标的属性 | 返回值类型 | 说明 |
---|---|---|
%ROWCOUNT | 整型 | 获得FETCH语句返回的数据行数 |
%FOUND | 布尔型 | 最近的FETCH语句返回一行数据则为真,否则为假 |
%NOTFOUND | 布尔型 | 与%FOUND属性返回值相反 |
%ISOPEN | 布尔型 | 游标已经打开时值为真,否则为假 |
其中%NOTFOUND是在游标中找不到元素的时候返回TRUE,通常用来判断退出循环。
CREATE OR REPLACE PROCEDURE STUDY AS
-- 声明游标
CURSOR v_data is select org_name,user_mobile from a_i_app_use_time ;
-- 声明变量去接收游标里的值
v_name a_i_app_use_time.org_name%TYPE;
v_mobile a_i_app_use_time.user_mobile%TYPE;
BEGIN
-- 打开游标
open v_data;
-- 遍历游标,注意要先fetch
LOOP
-- 获取游标数据赋值,注意赋值的顺序
FETCH v_data into v_name,v_mobile;
-- 结束循环的条件
exit when v_data%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name||'----'||v_mobile);
end loop;
-- 关闭游标
close v_data;
END STUDY;
基于参数做具体的查询,有点类似于多了个条件
create or replace PROCEDURE STUDY AS
-- 声明游标(带参数)
CURSOR v_data(id a_i_app_use_time.use_time_id%TYPE) is select org_name,user_mobile from a_i_app_use_time where use_time_id=id ;
-- 声明变量去接收游标里的值
v_name a_i_app_use_time.org_name%TYPE;
v_mobile a_i_app_use_time.user_mobile%TYPE;
BEGIN
-- 打开游标(输入参数的具体数值)
open v_data(517866);
-- 遍历游标
LOOP
-- 获取游标数据赋值,注意赋值的顺序
FETCH v_data into v_name,v_mobile;
-- 结束循环的条件
exit when v_data%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name||'----'||v_mobile); // 根据参数只查询到一条数据
end loop;
-- 关闭游标
close v_data;
END STUDY;
之前我们编写的PLSQL程序可以进行表的操作,判断、循环逻辑处理的工作,但无法重复调用。
可以理解为之前的代码全部编写在了main方法中,是匿名程序。Java可以通过封装对象和方法来解决服用问题。
PLSQL是将一个个PLSQL的业务处理过程存储起来进行复用,这些被存储起来的PLSQL程序称之为存储过程。
存储过程作用:
1、在一个开发过程中,为了一个特定的业务功能,会向数据库进行多次连接关闭(非常耗费资源),需要对数据库进行多次的I/O读写,性能比较低。如果把这些业务放到PLSQL中,在应用程序中,只需要调用PLSQL就可以做到连接关闭一次数据库就可以实现我们的业务,大大提高了效率。
2、ORACLE官方给的建议:能够让数据库操作的不要放在程序中,在数据库中实现基本不会出现错误,在程序中操作可能会存在错误(如果在数据库中操作数据,可以有一定的日志恢复功能)。
create or replace PROCEDURE 过程名称[(参数列表)] AS
BEGIN
END[过程名称];
```plsql
根据参数的类型,我们将其分为3类讲解:
1、不带参数的
2、带参数的
3、带输入输出(返回值)参数的
3.3 无参存储
**3.3.1 创建存储**
通过工具直接创建即可。
**3.3.2 调用存储过程**
```plsql
// set serveroutput on; -- 当调用时,显示过程已经完成,却没有显示结果时执行该条语句可解决
BEGIN
STUDY; -- 调用day02写好的名为STUDY的存储过程
STUDY; -- 又调用了一遍,写几遍即调用几遍
end;
【示例】
CREATE OR REPLACE PROCEDURE DEMOTEST ( ID IN NUMBER DEFAULT 517866) AS -- 入参给定一个默认值
v_name a_i_app_use_time.org_name%TYPE;
v_ym a_i_app_use_time.stat_ym%TYPE;
BEGIN
select org_name ,stat_ym into v_name ,v_ym FROM a_i_app_use_time where use_time_id=id;
DBMS_OUTPUT.put_line(v_name||v_ym); // 合肥供电公司20200506
END DEMOTEST;
===================================调用上面存储过程如下===================================
begin
DEMOTEST(517867);
end;
带输出参数的存储过程通常都是给第三方使用的,比如我们的java
CREATE OR REPLACE PROCEDURE DEMO_IN_OUT
(
ID IN NUMBER -- 输入参数作为条件
, V_YM OUT VARCHAR2 -- 输出参数,需返回
) AS
BEGIN
SELECT
stat_ym into v_ym FROM a_i_app_use_time where use_time_id=id;
END DEMO_IN_OUT;
================================调用上述存储过程=====================================
DECLARE -- 声明变量接收返回值
v_ym a_i_app_use_time.stat_ym%TYPE;
begin
DEMO_IN_OUT(517866,v_ym); -- 注意入参、出参的顺序
DBMS_OUTPUT.put_line(v_ym); // 20200506
end;
通常指带输出参数的存储过程,我们给一个参数,过程让其完成返回一个结果集,减少了网络了交互。
通过JDBC连接中的Connection的prepareCall方法来调用存储过程
//1. 加载驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//2. 获取连接对象
Connection conn=DriverManager.getConnect(url,user,password);
//3.获得语句对象
String sql=“{call 存储过程名称(?,?)}”; -- 转义sql,专门用来调取存储过程,这里有两个参数,两个 ? ?
Callableable call=conn.prepareCall(sql);
//4设置输入参数
call.setInt(1,100); -- 什么类型就设置什么类型,与之前sql无异
//5.注册输出参数
call.registerOutParameter(2,OracleTypes.Varchar); -- 注册类型对应Oracle里面的类型
//6.执行存储过程
call.execute();
//7.获取输出参数
String result=call.getString(2);