- PL/SQL(Procedure Language/SQL)
- PLSQL是Oracle对SQL语言的过程化扩展
- 指在SQL命令语言中增加了过程处理语句(如分支,循环等),使SQL语言具有过程处理能力
通过PLSQL Developer工具Test Windows创建程序模板或者通过语句在SQL Windows编写。
提示
:PLSQL语言是不区分大小写的PL/SQL可以分为三个部分:声明部分,可执行部分,异常处理部分
declare -- 声明变量,游标 i integer; begin -- 执行语句 -- [异常处理] end;
-- Created on 2021/10/1 by PRAY declare -- Local variables here i integer; begin dbms_output.put_line('HelloWorld'); end; ----------------------------------------- --DOS窗口打开输出 set serveroutput on
分类
普通数据类型
(char,vharchar2,date,number,boolean,long)特殊变量类型
(引用型变量,记录型变量)
变量名 变量类型(变量长度) 例如:v_name varchar2(20)
变量赋值的方式有两种:
- 直接赋值语句
:=
- 语句赋值,使用
select...into...赋值
(语法select值into变量)
-- Created on 2021/10/1 by PRAY declare --姓名 v_name varchar(20) := '张三'; --薪水 v_sal numeric; --地址 v_addr varchar(200); begin -- 直接赋值 v_sal := 1580; -- 语句赋值 select '珠海市' into v_addr from dual; dbms_output.put_line(v_name || '+' || v_sal || '+' || v_addr); end;
变量的类型和长度取决于表中字段的类型和长度
语法
:通过表名.列名%TYPE
指定变量的类型和长度,例如v_name emp.ename%TYPE
declare v_name emp.ename%TYPE; v_sal emp.sal%TYPE; begin select t.ename, t.sal into v_name, v_sal from emp t where t.empno = '7839'; dbms_output.put_line('姓名:' || v_name || ',薪水:' || v_sal); end;
引用型变量的好处:
使用普通变量的定义方式,需要知道表中列的类型,而使用引用类型,不需要考虑列的类型,使用%TYPE是非常好的编程风格,因为它使得PLSQL更加灵活,更加适应与对数据库定义的更新。
用于接收表中的一整行记录,相当于Java中的一个对象
语法
:变量名称,表名%ROWTYPE
例如:v_emp emp%rowtype;
declare v_emp emp%ROWTYPE; begin select * into v_emp from emp t where t.empno = '7839'; dbms_output.put_line('姓名:' || v_emp.ename || ',薪水:' || v_emp.sal); end;
如果一个表中,有100个字段,那么你程序如果要使用这100个字段的话,如果使用引用型变量一个个声明,会特别麻烦,记录型变量可以方便的解决这个问题。
记录型变量只能存储一个完整的行数据。
语法
IF 条件1 THEN 执行1 ELSIF 条件2 THEN 执行2 ELSE 执行3 END IF
案例
-- 根据emp的记录数 -- 如果记录数大于20,则输出对应的结果 -- 如果记录数10-20之间,则输出对应的结果 -- 如果记录数在10以下,则输出对应的结果 declare v_number number; begin select count(1) into v_number from emp t; if v_number > 20 then dbms_output.put_line('emp记录数大于20输出:' || v_number); elsif v_number >= 10 then dbms_output.put_line('emp记录数大于10输出:' || v_number); else dbms_output.put_line('emp记录数小于10输出:' || v_number); end if; end;
在ORACLE中有三种循环,
loop循环
,for循环
,while循环
-- 语法格式 LOOP EXIT WHEN 退出循环条件 END LOOP
案例
declare v_number number := 1; begin loop exit when v_number > 10; dbms_output.put_line(v_number); v_number := v_number + 1; end loop; end;
-- 案例 for i in 0..5 loop dbms_output.put_line(i); end loop;
declare i number; begin i := 2; while i<=5 loop dbms_output.put_line(i); i:=i+1; end loop; end;
用于临时存储一个查询返回的多行数据(结果集,类似于Java的JDBC连接返回的ResultSet集合)通过遍历游标,可以逐行访问处理该结果集的数据。
·
使用方式
声明,打开,读取,关闭
--游标声明 CURSOR 游标名[(参数列表)] IS 查询语句; --游标打开 OPEN 游标名 --游标读取 FETCH 游标名 INTO 变量列表; --游标关闭 CLOSE 游标名
游标的属性 | 返回值 | 说明 |
---|---|---|
%ROWCOUNT | 整型 | 获得FETCH语句返回的数据行数 |
%FOUND | 布尔型 | 最近的FETCH语句返回一行数据则为真,否则为假 |
%NOTFOUND | 布尔型 | 与%FOUND属性返回值相反 |
%ISOPEN | 布尔型 | 游标已经打开时为真,否则为假 |
其中,%NOTFOUND实在游标中找不到元素的时候返回TRUE,通常用来判断退出循环
-- 使用游标查询emp表中所有员工的姓名和工资,并将其依次打印出来 declare -- 声明游标 cursor c_emp is select t.ename,t.sal from emp t; -- 声明接收的变量 v_ename emp.ename%TYPE; v_sal emp.sal%TYPE; begin -- 打开游标 OPEN c_emp; LOOP -- 游标读取 FETCH c_emp INTO v_ename,v_sal; exit when c_emp%Notfound; dbms_output.put_line(v_ename ||'-' || v_sal); END LOOP; -- 关闭游标 CLOSE c_emp; end;
-- 使用游标查询emp表中所有员工的姓名和工资,并根据部门查询其对应的结果 declare -- 声明游标 cursor c_emp(v_deptno emp.deptno%TYPE) is select t.ename,t.sal from emp t where t.deptno = (v_deptno); -- 声明接收的变量 v_ename emp.ename%TYPE; v_sal emp.sal%TYPE; begin -- 打开游标 OPEN c_emp(30); LOOP -- 游标读取 FETCH c_emp INTO v_ename,v_sal; exit when c_emp%Notfound; dbms_output.put_line(v_ename ||'-' || v_sal); END LOOP; -- 关闭游标 CLOSE c_emp; end;
之前我们编写的PLSQL程序可以进行表的操作,判断,循环逻辑处理的工作,但无法重复调用。
可以理解之前的代码全部编写在了main方法中,是匿名程序,JAVA可以通过封装对象和方法来解决复用问题。
PLSQL是将一个个PLSQL的业务处理过程存储起来进行复用,这些被存储起来的PLSQL程序称之为
存储过程
。
存储过程的作用
:
- 在开发程序中,为了一个特定的业务功能,会想数据库进行多次连接关闭(连接和关闭是很耗费资源),需要对数据库进行多次I/O读写,性能比较低。如果把这些业务放到PLSQL中,在应用程序中只需要调用PLSQL就可以做的连接关闭一次数据库就可以实现我们的业务,可以大大提高效率。
- ORACLE官方建议:能够让数据库操作的不要放在程序中,在数据库中实现基本上不会出错,在程序中操作可能会存在错误(如果在数据库中操作数据,可以有一定的日志恢复等功能)
CREATE OR REPLACE PROCEDURE 过程名称[(参数列表)] is BEGIN END[过程名称]
根据参数的类型,分为3类
- 不带参数的
- 带输入参数的
- 带输入输出参数的
创建存储过程,打开PLSQL,Program Windows$\Rightarrow$Procedure
create or replace procedure p_helloworld is -- 在此声明变量 begin dbms_output.put_line('helloworld'); end p_helloworld; -- 调用存储过程,在plsql程序中,直接使用存储过程的名称。 begin p_helloworld; end; -- 调用存储过程,DOS窗口 exec p_helloworld; -- 如果没有输出,需要开启输出 set serveroutput on;
注意
:
- is和as是可以互用
- 存储过程中没哟declare关键字,declare用在语句块中
create or replace procedure p_query(i_empno in emp.empno%TYPE) as v_ename emp.ename%TYPE; v_sal emp.sal%TYPE; begin select t.empno,t.sal into v_ename,v_sal from emp t where t.empno = i_empno; dbms_output.put_line(v_ename || '-' || v_sal); end; -- 调用存储过程,在plsql程序中,直接使用存储过程的名称。 begin p_query(7839); end; -- 调用存储过程,DOS窗口 exec p_query(7839);
create or replace procedure p_query_out(i_empno in emp.empno%TYPE,o_sal out emp.empno%TYPE) as begin select t.sal into o_sal from emp t where t.empno = i_empno; end; -- 调用存储过程,在plsql程序中,直接使用存储过程的名称。 declare v_sal emp.sal%TYPE; begin p_query_out(7839,v_sal); dbms_output.put_line(v_sal); end;
public class ProcedureTest { public static void main(String[] args) throws Exception { //1.加载驱动 Class.forName("oracle.jdbc.driver.OracleDriver"); //2.获取连接对象 String url = "jdbc:oracle:thin:@192.168.168.128:1521:orcl"; String username = "scott"; String password = "tiger"; Connection connection = DriverManager.getConnection(url,username,password); //3.调用存储过程 String sql = "{call p_query_out(?,?)}"; //转义后的SQL语句 CallableStatement prepareCall = connection.prepareCall(sql); //4.设置输入参数 prepareCall.setInt(1, 7839); //5.注册输出参数 prepareCall.registerOutParameter(2, OracleTypes.DOUBLE); //6.执行存储过程 prepareCall.execute(); //7.获取输出参数 System.out.println("----------------------"); System.out.println(prepareCall.getDouble(2)); System.out.println("----------------------"); //8.关闭连接 prepareCall.close(); connection.close(); } }