PL/SQL
利用SQL进行编程
PL/SQL基础
PL/SQL块结构
PL/SQL块,由三部分组成
- 定义部分:用于定义常量、变量、游标、例外、复杂数据类型等
- 处理部分:用于实现应用模块功能,该部分包含了要执行的PL/SQL语句和SQL语句
- 例外部分:用于处理执行部分可能出现的运行错误
基本结构
declare --可选
begin
exception
end;
示例
set serveroutput on;
begin
--dbms_output 相当于java中的System.out
--put_line 相当于java中的println()
DBMS_OUTPUT.PUT_LINE('你好!PL/SQL');
end;
具有例外的块
示例
declare
--声明变量的时候需要指定具体的数据类型:java中声明一个变量:数据类型 变量名
--在PL/SQL中,声明一个变量是:变量名 数据类型(宽度)
v_name varchar(90);
begin
--相当于java中vname = 'select name from t_users where id = 11'
select name into v_name from T_USERS where ID = &no;
--相当于java中的System.out.println(vname)
DBMS_OUTPUT.PUT_LINE('名字:' || v_name);
exception
--相当于Java中的catch(no_data_found){DBMS_OUTPUT.PUT_LINE('没有找到数据');}
when no_data_found then
DBMS_OUTPUT.PUT_LINE('没有找到数据');
end;
PL/SQL块分类
- 匿名块
匿名块是指没有名字的PL/SQL块,可以内嵌到应用程序中,可以在在交互式环境中直接使用
示例
declare
v_avgSal number(6, 2);
begin
select avg(id) into v_avgSal from T_USERS where ID = &no;
DBMS_OUTPUT.put_line('平均:' || v_avgSal);
end;
- 命名块
命名块是指具有特定名称标识的PL/SQL块,只不过命名块比匿名块多了<<>>,加以标记。在使用嵌套的时候,可以加上命名块加以区分
示例
<<outer>>
declare
v_deptno number(2);
v_dname varchar2(10) ;
begin
<<inner>>
begin
select deptno into v_deptno from emp where lower(ename) = lower('&ename') ; end; -- inner 结束
select dname into v_dname from dept where deptno = v_deptno ;
dbms_output.put_line('部门名'|| v_dname ) ;
end ; -- outer 结束
- 子程序
- 触发器
定义并使用变量
变量类型
1. 复合类型:record、table、varray
2. 参照类型:cursor、object_type
3. LOB类型:BFILE、BLOB、CLOB、NCLOB
4. 标量类型:注意有boolean、其他一些数据类型(不列出来了)
语法结构
identified :[CONSTANT] datatype [NOT NULL ][:= | DEFAULT expr]
- identified :用于指定变量的命称
- CONSTANT :用于指定常量。当定义常量的时候,必须指定它的初始值,并且其数值不能更改
- datatype :用于指定变量或常量的数据类型
- NOT NULL : 用于强制初始化变量不能为null。当指定not null 选项时,必须要为变量提供数值。
- := :用于为变量或常量指定初始值。 DEAFAULT:用于为变量或常量指定初始值。
- expr : 用于指定初始值的PL/SQL表达式,可以是文本、其他变量、函数等。
示例
v_ename varchar2(90) ;
v_sal number(6 , 2 ) ;
v_kaka CONSTANT number(6,3) := 5.5 ;
v_valid Boolean not null default false ;
控制语句
分支语句
条件分支语句
语法结构
IF condition then
statements ;
[elsif condition then
statements ; ]
[else
statements ; ]
end if ;
- 简单条件判断
用于执行单一条件判断,如果满足特定条件,则会执行相应操作;如果不满足条件,则退出条件分支语句。简单条件判断是使用if-then语句来完成的
示例
declare
v_count number(9);
begin
select id into v_count from t_users where id = &no;
if v_count > 10 then
dbms_output.put_line('满足:' || v_count);
end if;
end;
- 二重条件分支
二重条件分支是根据条件来选择两种可能性,当使用二重条件分支的时候,如果满足条件,则执行一组操作;如果不满足条件,则执行另外一组操作。二重条件分支是利用if…then…else 来完成的
示例
declare
v_count number(9);
begin
select count(id) into v_count from t_users;
if v_count > 10 then
dbms_output.put_line('满足的添加');
else
dbms_output.put_line('不满足的');
end if;
end;
- 多重条件分支
用于执行最复杂的条件分支操作,当使用多重条件分支时,如果满足第一个条件,则执行第一种操作,如果不满足第一个条件,则检查是否满足第二个条件,如果满足第二个条件,则执行第二种操作;如果不满足第二个条件,则检查是否满足第三个条件,以此类推。多重条件分时是使用if…then…elsif语句来完成的
示例
declare
v_count T_USERS.ID%type;
v_name T_USERS.NAME%type;
begin
select id, name into v_count , v_name from t_users where id = &no;
if v_count = 10 then
dbms_output.put_line('满足1:' || v_count);
elsif v_count = 9 then
dbms_output.put_line('满足2:' || v_count);
else
dbms_output.put_line('不满足');
end if;
end;
case语句
语法结构
--根本语法
case
when selector = condition1 then statement1;
when selector = condition2 then statement2;
...
else statementn+1;
end case;
- 在CASE语句种使用单一选择符进行等值比较
如果条件选择符完全相同,并且条件表达式为相等条件选择,那么可以使用单一条件选择符进行等值比较
示例
declare
v_name T_USERS.NAME%type;
begin
select name into v_name from T_USERS where id = &no;
case v_name
when '张三丰' then
dbms_output.put_line('1');
when '宋远桥' then
dbms_output.put_line('2');
else
dbms_output.put_line('3');
end case;
end ;
- 在CASE语句种使用多种条件比较
当使用单一条件选择符进行等值比较时,可以使用CASE selector语法来实现。如果包含有多种条件进行不等比较,那么必须在when子句中指定比较条件
示例
declare
v_name T_USERS.NAME%type;
v_id T_USERS.ID%type;
begin
select name , id into v_name , v_id from T_USERS where id = &no;
case
when v_name = '张三丰' or v_name = '张翠山' then
dbms_output.put_line('1');
when v_name = '宋远桥' or v_id between 7 and 10 then
dbms_output.put_line('2');
else
dbms_output.put_line('3');
end case;
end ;
循环语句
基本循环
在PL/SQL中最简单格式的循环语句时基本循环语句,这种循环语句以loop开始,以end loop结束
语法结构
loop
statement1 ;
...
exit [when condition];
end loop ;
类似java中的do...while循环
示例
declare
--赋值
v_count INT := 1 ;
begin
LOOP
dbms_output.put_line( v_count );
EXIT WHEN v_count = 10 ;
v_count := v_count + 1 ;
END LOOP ;
end ;
while循环
对于while循环来说,只有条件为true时,才会执行循环体内的语句。while循环以while…loop开始,以end loop结束
语法结构
while condition loop
statement1;
statement2 ;
...
end loop;
类似java中的while循环
示例
declare
i int :=1;
begin
while i <= 10 loop
i := i + 1;
DBMS_OUTPUT.put_line('' || i) ;
end loop;
end;
for循环
语法结构
for 变量 in [reverse] 下界值...上界值 loop
循环体
end loop ;
类似java中的fori循环(取等值)
示例1
declare
i number:=0;
sumResult number:=0;
begin
for i in 1..100 loop
sumResult:=sumResult+i;
end loop;
DBMS_OUTPUT.put_line('sum:' || sumResult);
end;
示例2
declare
i number:=0;
begin
for i in reverse 1..100 loop
DBMS_OUTPUT.put_line('i:' || i);
end loop;
end;
嵌套循环和标号
嵌套循环是指在一个循环语句之中嵌入另一个循环语句,而标号(Label)则用于标记嵌套块或嵌套循环。通过嵌套循环中使用标号,可以区分内层循环和外层循环,并且可以在内层循环中直接退出外层循环
示例
--九九乘法表
declare
result int;
begin
<<outer>> --外层循环
for i in 1..9 loop
<<inner>> --内层循环
for j in 1..i loop
result := i*j;
DBMS_OUTPUT.put(i || '*' || j || '=' || result || ' ');
end loop;
DBMS_OUTPUT.put_line(' ');
end loop;
end;
顺序控制语句
goto
goto语句用于跳转到特定标号处去执行语句。使用Goto语句会增加程序的复杂性,并且使得应用程序可读性变差,所以开发应用程序时,一般都建议用户不要使用GOTO语句
示例
declare
i int:=0;
begin
for i in 1..10 loop
if i=5 then
goto end_loop;--等于5时进行跳转到<<end_loop>>,直接跳出循环
end if;
DBMS_OUTPUT.put_line('i:' || i);
end loop;
<<end_loop>>
DBMS_OUTPUT.put_line('goto');
end;
null
null语句不会执行任何操作,并且会直接将控制传递到下一条语句。使用null语句的主要好处是可以提高PL/SQL程序的可读性
示例
declare
v_sal emp.sal%TYPE;
v_ename emp.ename%TYPE;
begin
select ename , sal into v_ename , v_sal from emp where empno=&no;
if v_sal<3000 then
update emp set comm= sal*0.1 where ename=v_ename;
else
null;
end if ;
end;
例外
例外类似java中的异常。
例外(Exception)是一种标识符,如果运行PL/SQL块是出现错误或警告,则会触发例外,当例外触发时,默认会终止PL/SQL块的执行,通过PL/SQL块中引入例外处理部分,可以捕捉到各种例外,并根据例外出现的情况进行相应的处理
例外分类
- 预定义例外:定义好的例外,类似于java中已经定义好的1异常,比如IndexOutOfBoundsException
- 非预定义例外:非预定义例外用于处理与预定义里无关的Oracle错误,就是不知道的错误
- 自定义例外:指由PL/SQL开发人员所定义的例外,相当于java中的自定义异常
处理例外
处理预定义例外
示例
declare
pnum number;
begin
pnum:=1/0;
exception
--类似于catch
when zero_divide then
dbms_output.put_line('0不能做除数');
when value_error then
dbms_output.put_line('算术或转换例外');
when others then
dbms_output.put_line('产生了其他例外');
end;
处理非预定义例外
示例
declare
v_name EMP.ENAME%type;
e_info exception ;
pragma exception_init ( e_info ,100);
begin
select ENAME into v_name from EMP where EMPNO = &no;
dbms_output.put_line('1' || v_name);
exception
when e_info then
DBMS_OUTPUT.put_line( SQLCODE );
DBMS_OUTPUT.put_line( SQLERRM );
dbms_output.put_line('未找到数据');
end;
处理自定义例外
示例
declare
e_info exception ;
pragma exception_init ( e_info ,-2291);
e_emp_no exception ;
begin
update emp set DEPTNO=&no where EMPNO=&no;
if SQL%NOTFOUND then
--引发异常的两种方式
--相当于java中的throw
--1.raise
raise e_emp_no;
--相当于java中的throw new RuntimeException( e , message )
--有两个参数,错误代码和错误信息
--2.raise_application_error内置过程
raise_application_error(-2291,'找不到数据');
end if;
exception
when e_info then
dbms_output.put_line('部门不存在');
when e_emp_no then
DBMS_OUTPUT.put_line('雇员不存在');
end;
PL/SQL记录
记录(record ) 一组相关的记录成员(Field)组成,类似于Java中的类
定义记录
自定义记录
当使用自定义的PL/SQL记录时,需要分别定义记录类型和记录变量
语法结构
TYPE 记录名 IS RECORD(
// 记录的字段
字段名(变量名) 数据类型
)
示例
declare
type emp_record is record(
v_id emp.empno%type,
v_name emp.ename%type,
v_job emp.job%type
);
emp_info_instance emp_record;
begin
--查询数据并给实例中的各个属性赋值
select EMPNO,ENAME,job
into emp_info_instance.v_id,
emp_info_instance.v_name,
emp_info_instance.v_job
from emp where empno=&no;
DBMS_OUTPUT.put_line(emp_info_instance.v_id);
DBMS_OUTPUT.put_line(emp_info_instance.v_name);
DBMS_OUTPUT.put_line(emp_info_instance.v_job);
end;
类似于java中:
public recode emp_record(Integer v_id,String v_name,String v_job){}
public class test{
public static void main(String[] args){
// 在控制台 接收到了 no 的值 : Scanner
// 根据 no 的值 获取到 一条数据, 只不过是 仅仅获取到的是 v_id、v_name、v_job
emp_info emp_info_instance = new emp_info() ;
// 利用 set 方法 进行传入指定的值
// 输出具体的内容。
}
}
使用%rowtype属性定义记录变量
%ROWTYEP属性可以基于表或视图定义记录变量。当使用该属性定义记录变量时,记录成员的名称和类型与表或视图列的名称和类型完全相同
语法结构
identifier table_name%ROWTYEP ;
或
identifer view_name%ROWTYEP ;
示例
declare
emp_info_instance EMP%rowtype;
begin
--查询数据并给实例中的各个属性赋值
--如果要获取所有列,可以用*来代替
select EMPNO,ENAME,job
into emp_info_instance.EMPNO,
emp_info_instance.ENAME,
emp_info_instance.JOB
from emp where empno=&no;
DBMS_OUTPUT.put_line(emp_info_instance.EMPNO);
DBMS_OUTPUT.put_line(emp_info_instance.ENAME);
DBMS_OUTPUT.put_line(emp_info_instance.JOB);
end;
使用记录
在select into 语句中使用PL/SQL记录
示例
declare
type user_info is record(
id t_users.id%type,
name t_users.name%type,
password t_users.password%type
);
user_record user_info;
begin
--在Select into 语句中使用记录变量
--当在select into 语句种直接使用记录变量是,选择列表中的列和表达式的顺序、个数、类型必须要与记录成员的顺序、个数、类型完全匹配
select id,name,password into user_record from T_USERS where ID =&no;
DBMS_OUTPUT.put_line(user_record.id || ' ' ||user_record.name || ' ' || user_record.password);
--在select into 语句种使用记录成员
--当在select into 语句种直接使用记录成员的时候,选择列表后列和表达式的顺序可以任意指定,但记录成员需要与之匹配
select id,name,password into user_record.id,user_record.name,user_record.password from T_USERS where ID =&no;
DBMS_OUTPUT.put_line(user_record.id || ' ' ||user_record.name || ' ' || user_record.password);
end;
在 DML 操作中使用 记录
- 在insert 语句种使用PL/SQL记录
示例
declare
user_info T_USERS%rowtype;
begin
--在values子句中使用记录变量
--当在values子句中使用记录变量插入数据是,列的顺序、个数、类型必须要与记录成员的顺序、个数、类型完全匹配
user_info.ID := 26;
user_info.NAME :='风清扬';
insert into T_USERS values user_info;
--在values子句中使用记录成员
--当在values子句中使用记录成员插入数据是,类的顺序可以任意指定,但记录成员需要与之匹配
user_info.ID := 27;
user_info.NAME :='独孤';
insert into T_USERS(id,name) values(user_info.ID,user_info.NAME);
end;
- 在update语句中使用PL/SQL
示例
declare
user_info T_USERS%rowtype;
begin
--在set子句中使用记录变量
--当在set子句中使用记录变量更新数据时,列的顺序、个数、类型必须要与记录成员的顺序、个数、类型完全匹配
user_info.PASSWORD := 123456;
user_info.PHONE := 12345678;
user_info.WEIGHT := 100;
update T_USERS set row = user_info where ID = 26 ;
--在set子句中使用记录成员
--在set子句中使用记录成员更新数据是,列的顺序可以任意指定,但是记录成员需要与之匹配
user_info.NAME := '萧别离';
update T_USERS set NAME = user_info.NAME where ID = 27;
end;
- 在delete 语句中使用PL/SQL记录
当使用PL/SQL记录删除数据时,只能在delete语句的where子句中使用记录成员
示例
declare
user_info T_USERS%rowtype;
begin
user_info.ID := 26 ;
delete from T_USERS where ID = user_info.ID ;
end;
PL/SQL集合
为了处理单列多行数据,开发人员可以使用PL/SQL集合
集合类型
索引表(PL/SQL表)
索引表,也称PL/SQL表,它是早期处理PL/SQL数组的数据类型。注意:元素个数没有限制,并且下标可以为负值。索引表只能作为PL/SQL复合数据类型使用,而不能作为表列的数据类型使用
语法结构
TYPE 类型名称 IS TABLE OF 元素的类型
[NOT NULL ] INDEX BY 索引的类型 ;
变量 类型名称 ;
索引的类型可以是: binary_interger 、 PLS_INTEGER 、 varchar2
类似于java中的Map
示例
--类似Map<Integer,String>,索引为Map的key
declare
type user_index is table of t_users.name%type
index by binary_integer;
user_info user_index;
begin
select name into user_info(1) from T_USERS where ID = &no;
--第一个元素
DBMS_OUTPUT.put_line(user_info(1));
--count : 索引表中 有多少个元素
DBMS_OUTPUT.put_line(user_info.COUNT);
--first : 第一个元素 的下标
DBMS_OUTPUT.put_line(user_info.FIRST);
--last : 最后一个元素 的下标
DBMS_OUTPUT.put_line(user_info.LAST);
end;
嵌套表(Nested Table)
嵌套表也是一种用于处理PL/SQL数组的数据类型,下标从1开始,并且元素没有限制,元素值可以是稀疏的。注意:索引表类型不能作为表列的数据类型使用,但是嵌套表类型可以作为表列的数据类型使用
语法结构
type 类型名称 is table of 元素类型。
变量 类型名称 ;
类似于java中的数组,不过下标从1开始
注意:
- 如果想使用嵌套表, 那么首先需要初始化 : 变量 类型名称 := 类型名称( 元素… )
- 元素没有限制 指代的是 在初始化阶段 元素的个数没有限制
- 一旦 通过 初始化 确定了 元素的个数,那么 长度就不可以改变
- 下标 的范围是 1 ~ 元素的个数(count)
示例
declare
type user_info is table of t_users.name%type;
user_table user_info;
begin
user_table :=user_info('name1','name2');
DBMS_OUTPUT.put_line(user_table(1));
DBMS_OUTPUT.put_line(user_table(2));
select name into user_table(1) from T_USERS where ID = &first;
select name into user_table(2) from T_USERS where ID = &second;
--count : 索引表中 有多少个元素
DBMS_OUTPUT.put_line(user_info.COUNT);
--first : 第一个元素 的下标
DBMS_OUTPUT.put_line(user_info.FIRST);
--last : 最后一个元素 的下标
DBMS_OUTPUT.put_line(user_info.LAST);
for i in 1..user_table.COUNT
loop
DBMS_OUTPUT.put_line(user_table(i));
end loop;
end;
变长数组(varray)
VARRY也是一种用于处理PL/SQL数组的数据类型,它也可以作为表列的数据类型使用。其元素下标从1开始,并且元素的最大个数是有限制的
语法结构
TYPE 类型名称 IS VARRY(最大长度限制) OF 元素类型 [NOT NULL];
变量 类型名称 ;
注意:
- 如果想使用变长数组, 那么首先需要初始化 : 变量 类型名称 := 类型名称( 元素… )
- 初始化的时候,可以初始化 任意个元素的数量 , 但是不能超过最大值
- 具体的变长数组的 个数 由 初始化 元素的个数 决定
- 下标 的范围是 1 ~ 元素的个数(count)
示例
declare
type user_info is varray(255) of t_users.name%type;
user_table user_info;
begin
user_table :=user_info('name1','name2');
DBMS_OUTPUT.put_line(user_table(1));
DBMS_OUTPUT.put_line(user_table(2));
select name into user_table(1) from T_USERS where id = &first;
select name into user_table(2) from T_USERS where id = &second;
for i in 1..user_table.COUNT
loop
DBMS_OUTPUT.put_line(user_table(i));
end loop;
end;
记录表
记录表就是 索引表的一种变形,可以处理 多行 多列数据
PL/SQL 变量用于处理单行单列数据 ;PL/SQL 记录用于处理单行多列数据 ;PL/SQL 集合(索引表、嵌套表、VARRAY) 用于处理多行单列数据。而PL/SQL记录表可以有效的处理多行多列数据(记录的集合或数组)定义记录表的类型
语法结构
-- 先声明一个 记录
TYPE record_type_name IS RECORD ( field_declaration [ , ...... ] ) ;
-- 声明一个 由 记录 组成的 索引表
TYPE 表的类型 IS TABLE OF 记录的类型 INDEX BY binary_integer ;
示例1
--所有数据
declare
type user_info is table of t_users%rowtype index by binary_integer;
user_table user_info;
begin
select * into user_table(1) from T_USERS where id = &first;
select * into user_table(2) from T_USERS where id = &second;
select * into user_table(3) from T_USERS where id = &third;
for i in 1..3
loop
DBMS_OUTPUT.put_line(user_table(i).name || ' ' || user_table(i).id);
end loop;
end;
示例2
--指定数据
declare
type user_record_info is record(name t_users.name%type,id t_users.id%type);
type user_info is table of user_record_info index by binary_integer;
user_table user_info;
begin
select name,id into user_table(1) from T_USERS where id = &first;
select name,id into user_table(2) from T_USERS where id = &second;
select name,id into user_table(3) from T_USERS where id = &third;
for i in 1..3
loop
DBMS_OUTPUT.put_line(user_table(i).name || ' ' || user_table(i).id);
end loop;
end;
集合方法
- exists:此方法用于确认集合元素是否存在
- count:此方法用于返回当前集合变量中的元素总个数
- limit :此方法用于返回集合元素的最大个数
- FIRST和LAST:FIRST方法返回集合变量第一个元素的下标,而LAST返回集合变量最后一个元素的下标
- PRIOR和NEXT方法:prior返回当前集合的前一个元素的下标,而next方法则用于返回当前元素集合的后一个元素的下标
- extend: 用于扩展集合变量的尺寸,并为它们增加元素。该方法有extend,extend(n),extend(n,i)等三种调用格式,
- 只适用于嵌套表和VARRAY
- trim :裁剪元素该方法用于从集合尾部删除元素,它有TRIM和TRIM(n)两种调用格式
- delete: 删掉某元素该方法用于删除集合元素,但该方法只适用于嵌套表和索引表,而不适用于VARRAY
语法结构
集合名字.方法名字 [(参数)]
示例
declare
--嵌套表
type user_info is table of t_users.NAME%type;
user_table user_info;
--变长数组
type user_info1 is varray(255) of t_users.name%type;
user_table1 user_info1;
begin
user_table :=user_info('name','name1','name2','name3','name4');
--exists
if user_table.EXISTS(1) then
DBMS_OUTPUT.put_line('1位置元素存在');
else
DBMS_OUTPUT.put_line('1位置元素不存在');
end if;
--count
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
--first
DBMS_OUTPUT.put_line('第一个元素的下标为:' || user_table.FIRST);
--last
DBMS_OUTPUT.put_line('最后一个元素的下标为:' || user_table.LAST);
--prior
DBMS_OUTPUT.put_line('2号元素的前一个元素下标为:' || user_table.PRIOR(2));
--next
DBMS_OUTPUT.put_line('1号元素的后一个元素下标为:' || user_table.NEXT(1));
--extend
--增加一个null元素
user_table1.extend;
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
--增加5个null元素
user_table1.extend(5);
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
--增加5个下标为2的那个元素
user_table1.extend(5,2);
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
--trim
--裁剪最后一个元素
user_table.trim();
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
--裁剪5个元素,从最后一个元素开始起
user_table.trim(5);
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
--delete
--删除所有元素
user_table.DELETE();
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
--删除第5个元素元素
user_table.DELETE(5);
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
--删除从2到5的元素
user_table.DELETE(2,5);
DBMS_OUTPUT.put_line('元素的个数是:' || user_table.COUNT);
end;
集合赋值
将一个集合的数据赋值给另一个集合
当使用赋值语句( := ) 或SQL语句将源集合中的数据赋值给目标集合时,会自动清除目标集合原有的数据,并将源集合中的数据赋值给该目标语句
示例
declare
--嵌套表
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2',null,null);
user_table1 user_info:=user_info(null,null);
user_table2 user_info;
user_table3 user_info;
begin
--变量重新赋值
user_table2 :=user_table1;
for i in 1..user_table2.COUNT
loop
DBMS_OUTPUT.put_line(user_table2(i));
end loop;
DBMS_OUTPUT.put_line('----------------');
user_table2 :=user_table;
for i in 1..user_table2.COUNT
loop
DBMS_OUTPUT.put_line(user_table2(i));
end loop;
--给集合赋予null值
user_table2 :=user_table3;
if user_table2 is null then
DBMS_OUTPUT.put_line('该集合为空');
end if;
end;
使用集合操作符给嵌套表赋值
在编写PL/SQL程序时允许将多个嵌套表的结果组合到某个嵌套表中,这项任务通过使用ANSI集合操作符(SET,MULTISET UNION , MULTISET INTERSECT , MULTISET EXCEPT )来完成
- 使用SET操作符
SET操作符用于取消特定嵌套表中的重复值。下面以去掉嵌套表nt_table的重复元素为例,说明使用SET操作符的方法
示例
declare
--嵌套表
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4','2');
result user_info;
begin
result:=set(user_table);
DBMS_OUTPUT.put_line('使用set之后的结果:');
for i in 1..result.count loop
DBMS_OUTPUT.put_line(result(i));
end loop;
end;
- 使用MULTISET UNION 操作符
MULTISET UNION 用于取得两个嵌套表的并集。当使用该操作符合并嵌套表结果时,结果集中会包含重复值
示例
declare
--嵌套表
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4','2');
user_table1 user_info:=user_info('3','1','2','4','2');
result user_info;
begin
result:=user_table multiset union user_table1;
DBMS_OUTPUT.put_line('使用MULTISET UNION之后的结果:');
for i in 1..result.count loop
DBMS_OUTPUT.put_line(result(i));
end loop;
end;
- 使用MULTISET UNION DISTINCT 操作符
MULTISET UNION DISTINCT 用于取得两个嵌套表的并集,并取消重复
示例
declare
--嵌套表
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4','2');
user_table1 user_info:=user_info('3','1','2','4','2');
result user_info;
begin
result:=user_table multiset union distinct user_table1;
DBMS_OUTPUT.put_line('使用MULTISET UNION DISTINCT之后的结果:');
for i in 1..result.count loop
DBMS_OUTPUT.put_line(result(i));
end loop;
end;
- 使用MULTISET INTERSECT 操作符
获取两个嵌套表的交集
示例
declare
--嵌套表
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4','2');
user_table1 user_info:=user_info('3','1','2','4','2');
result user_info;
begin
--加distinct可去重
result:=user_table multiset intersect distinct user_table1;
DBMS_OUTPUT.put_line('使用MULTISET INTERSECT之后的结果:');
for i in 1..result.count loop
DBMS_OUTPUT.put_line(result(i));
end loop;
end;
- 使用MULTISET EXCEPT 操作符
获取两个嵌套表的差集,在嵌套表1中存在,但是在嵌套表2中不存在的元素
示例
declare
--嵌套表
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4','2','5','5');
user_table1 user_info:=user_info('3','1','2','4','2');
result user_info;
begin
--加distinct可去重
result:=user_table multiset except distinct user_table1;
DBMS_OUTPUT.put_line('使用MULTISET INTERSECT之后的结果:');
for i in 1..result.count loop
DBMS_OUTPUT.put_line(result(i));
end loop;
end;
比较集合
检测集合是否为null
可以使用 is null 检测 嵌套表和 varray ;但是 如果仅仅检测 嵌套表可以使用 is empty
示例1
--is null
declare
--嵌套表
type user_info is table of varchar(255);
user_table user_info;
begin
if user_table is null then
DBMS_OUTPUT.put_line('user_table 尚未初始化');
end if;
end;
示例2
--is empty
declare
--嵌套表
type user_info is table of varchar(255);
user_table user_info;
begin
if user_table is empty then
DBMS_OUTPUT.put_line('user_table 尚未初始化');
end if;
end;
比较嵌套表是否相同
可以使用 = 或 != 检测 嵌套表
示例
declare
type user_info is table of varchar(255);
user_table user_info:=user_info('scott');
user_table1 user_info:=user_info('scote');
begin
if user_table = user_table1 then
DBMS_OUTPUT.put_line('两个嵌套表完全相等');
else
DBMS_OUTPUT.put_line('两个嵌套表不相等');
end if;
end;
在嵌套表上使用集合操作符
在嵌套表上使用ANSI集合操作符,这些操作符只适用于嵌套表,而不适用于VARRAY和索引表
- 使用操作符CARDINALITY
函数CARDINALITY 用于返回嵌套表的元素个数
示例
declare
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4');
begin
DBMS_OUTPUT.put_line('嵌套表的元素个数:' || cardinality(user_table));
end;
- 使用操作符 SUBMULTISET OF
确定一个嵌套表是否为另一个嵌套表的子集
示例
declare
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4');
user_table1 user_info:=user_info('1','2','3');
begin
if user_table1 submultiset of user_table then
DBMS_OUTPUT.put_line('user_table1 是 user_table 的子集');
end if;
end;
- 使用操作符 MEMBER OF
用于检测特定数据是否为嵌套表的元素
示例
declare
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4');
flag boolean;
v varchar(10) :='2';
--声明函数实现
function bool2char(bool in Boolean) return varchar is
begin
if bool then
return 'true';
else
return 'false';
end if;
end;
begin
--调用函数
flag:=v member of user_table;
DBMS_OUTPUT.put_line(bool2char(flag));
--普通实现
if v member of user_table then
DBMS_OUTPUT.put_line(v || ' 是 user_table 的元素');
end if;
end;
- 使用操作符 IS A SET
用于检测嵌套表是否包含重复的元素值
示例
declare
type user_info is table of varchar(255);
user_table user_info:=user_info('1','2','3','4');
begin
if user_table is a set then
DBMS_OUTPUT.put_line('嵌套表没有重复的元素值:');
end if;
end;
批量绑定
批量绑定是指执行单次SQL操作能传递所有集合元素的数据。都在SELECT,INSERT,UPDATE,DELETE语句上处理批量数据时,通过批量绑定,可以极大地加快数据库处理速度,提高应用程序的性能
批量绑定是使用 BULK COLLECT 子句和 FORALL 语句来完成,其中BULK COLLECT子句用户取得批量数据,该子句只能用于SELECT语句、FETCH语句和DML返回子句中;而FORALL语句只适用于执行批量的DML操作
FORALL语句
语法一
forall index in lower_bound.. upper_bound sql_statement ;
-- 其中index是隐含定义的整数变量(将作为集合元素下标被引用);
-- lower_bound和upper_bound分别是集合元素的上界和下界
示例1
- 在INSERT语句上使用批量绑定
declare
type user_info is table of t_goods.id%type index by binary_integer;
v_id user_info;
type user_info1 is table of t_goods.name%type index by binary_integer;
v_name user_info1;
type user_info2 is table of t_goods.price%type index by binary_integer;
v_price user_info2;
start_time number(10);
end_time number(10);
begin
for i in 1..5000 loop
v_id(i) :=i+2;
v_name(i) :='张三' || to_char(i);
v_price(i):=i;
end loop;
start_time :=DBMS_UTILITY.GET_TIME();
--forall
forall i in 1..5000
insert into t_goods values (v_id(i),v_name(i),v_price(i));
commit ;
end_time := DBMS_UTILITY.GET_TIME();
DBMS_OUTPUT.put_line('消耗了:' || (end_time - start_time)/100);
end;
语法二
forall index in indices of collecion [between lower_bound.and. upper_bound] sql_statement ;
-- 其中indices of 子句用于指定只取得对应与collection集合元素下标的index的值
示例2
- 在update语句上使用批量绑定
declare
type user_info is table of t_goods.id%type index by binary_integer;
v_id user_info;
type user_info1 is table of t_goods.name%type index by binary_integer;
v_name user_info1;
start_time number(10);
end_time number(10);
begin
for i in 1..5000 loop
v_id(i) :=i+2;
v_name(i) :='张三' || to_char(i);
end loop;
start_time :=DBMS_UTILITY.GET_TIME();
--forall
forall i in indices of v_id
update t_goods set name = v_name(i) where id = v_id(i);
commit ;
end_time := DBMS_UTILITY.GET_TIME();
DBMS_OUTPUT.put_line('消耗了:' || (end_time - start_time)/100);
end;
语法三
forall index in values of index_collection sql_statement ;
-- values of 子句用于指定index值从集合变量index——collection中取得。
示例3
- 在delete语句上使用批量绑定
declare
type user_info is table of t_goods.id%type index by binary_integer;
v_id user_info;
type user_info3 is table of pls_integer;
index_save user_info3:=user_info3(1,2,3,4,5,6);
start_time number(10);
end_time number(10);
begin
for i in 1..5000 loop
v_id(i) :=i+2;
end loop;
start_time :=DBMS_UTILITY.GET_TIME();
--forall
forall x in values of index_save
delete from t_goods where id = v_id(x);
commit ;
end_time := DBMS_UTILITY.GET_TIME();
DBMS_OUTPUT.put_line('消耗了:' || (end_time - start_time)/100);
end;
使用SQL%BULK_ROWCOUNT属性
用于取得在执行批量绑定操作时第i个元素所作用的行数
示例
declare
type user_info is table of number(3);
user_table user_info := user_info(10,20);
begin
forall i in 1..user_table.COUNT
update T_USERS set weight = WEIGHT * 1.1 where ID = user_table(i);
DBMS_OUTPUT.put_line('第二个元素所作用的行数为:' ||sql%bulk_rowcount(2));
end;
BULK COLLECT子句
BULK COLLECT子句用于取得批量数据,它只适用于SELECT INTO语句、FETCH INTO语句和DML返回子句
在SELECT INTO语句中使用BULK COLLECT子句
可以 将 一行 多列 或 多行多列数据 放入到 集合中
语法结构
select xxxxxx bulk collect into 集合 from 表名
示例
declare
type user_info is table of t_users%rowtype index by binary_integer;
user_table user_info;
begin
select * bulk collect into user_table from T_USERS where id = &no;
for i in 1..user_table.COUNT loop
DBMS_OUTPUT.put_line('名字:' || user_table(i).NAME);
end loop;
end;
在DML的返回子句中使用BULK COLLECT 子句
执行DML操作时会改变数据库数据。为了取得DML操作所改变的数据,可以使用RETURNING子句。为了取得DML所作用的多行数据,需要使用BULK COLLECT子句
示例1
- 插入语句
declare
type user_info is table of t_goods.id%type index by binary_integer;
v_id user_info;
type user_info1 is table of t_goods.name%type index by binary_integer;
v_name user_info1;
type user_info2 is table of t_goods.price%type index by binary_integer;
v_price user_info2;
type user_info3 is table of t_goods%rowtype index by binary_integer;
ids user_info3;
begin
for i in 1..50 loop
v_id(i) :=i+2;
v_name(i) :='张三' || to_char(i);
v_price(i):=i;
end loop;
--forall
forall i in 1..v_id.COUNT
insert into t_goods(id,name,price) values (v_id(i),v_name(i),v_price(i))
returning id , name , price bulk collect into ids;
for i in 1..ids.count loop
DBMS_OUTPUT.PUT_LINE( ids(i).id || ' : ' || ids(i).NAME || ':' || ids(i).PRICE) ;
end loop;
end;
示例2
- 更新语句
declare
type user_info is table of t_goods.id%type index by binary_integer;
v_id user_info;
type user_info1 is table of t_goods.name%type index by binary_integer;
v_name user_info1;
type user_info2 is table of t_goods.price%type index by binary_integer;
v_price user_info2;
type user_info3 is table of t_goods%rowtype index by binary_integer;
ids user_info3;
begin
for i in 1..50 loop
v_id(i) :=i+2;
v_name(i) :='张三' || to_char(i + 50);
v_price(i):=i;
end loop;
--forall
forall i in 1..v_id.COUNT
update t_goods set name = v_name(i) where id = v_id(i)
returning id ,name ,price bulk collect into ids;
for i in 1..ids.count loop
DBMS_OUTPUT.PUT_LINE( ids(i).id || ' : ' || ids(i).NAME || ':' || ids(i).PRICE) ;
end loop;
end;
示例3
- 删除语句
declare
type user_info is table of t_goods.id%type index by binary_integer;
v_id user_info;
type user_info1 is table of t_goods.name%type index by binary_integer;
v_name user_info1;
type user_info2 is table of t_goods.price%type index by binary_integer;
v_price user_info2;
type user_info3 is table of t_goods%rowtype index by binary_integer;
ids user_info3;
begin
for i in 1..50 loop
v_id(i) :=i+2;
v_name(i) :='张三' || to_char(i + 50);
v_price(i):=i;
end loop;
--forall
forall i in 1..v_id.COUNT
delete from t_goods where id = v_id(i)
returning id ,name ,price bulk collect into ids;
for i in 1..ids.count loop
DBMS_OUTPUT.PUT_LINE( ids(i).id || ' : ' || ids(i).NAME || ':' || ids(i).PRICE) ;
end loop;
end;
游标
概念:
plsql的两种游标类型:
使用显示游标的步骤:
- 声明游标:cursor 游标的名字 is 一个查询语句 ;
- 打开游标:open 游标的名字 ;
- 提取游标:使用fetch 进行提取数据 ;
语法分类:
- 提取数据到 一个变量 中,一般是用来 处理没有关系 的数据:fetch 游标名字 into 变量名 ;
- 提取数据到 一个记录 中,一般是用来 处理有关系的 数据:fetch 游标名字 into 记录名 ;
- 提取数据到 集合 中,形式:fetch 游标名字 bulk collect into 集合 [limit rows] ;
- 关闭游标:close 游标的名字 ;
游标的属性
- %found:
- %notfound
- %isopen
- %rowcount:
示例1:
-- 语法1
-- fetch into 变量
declare
id T_USERS.id%type;
name T_USERS.name%type;
-- 声明一个游标
cursor user_cursor is select id , name from T_USERS;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
open user_cursor;
end if;
-- user_cursor%found 在调用 fetch 之前,一直是false
-- 开始循环,如果找到了数据,那么就加入循环
loop
-- 调用一次就移动一下
fetch user_cursor into id , name ;
-- 退出条件
exit when user_cursor%notfound;
DBMS_OUTPUT.put_line(id) ;
DBMS_OUTPUT.PUT_LINE(name) ;
DBMS_OUTPUT.PUT_LINE('当前行数:' || user_cursor%rowcount) ;
end loop;
-- 关闭游标
close user_cursor;
end;
示例2:
-- 语法2
-- fetch into 记录名
declare
-- 声明一个记录,t_users的数据比要提取的数据要多,用*获取
user_record T_USERS%rowtype;
-- 声明一个游标
cursor user_cursor is select * from T_USERS;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
open user_cursor;
end if;
-- user_cursor%found 在调用 fetch 之前,一直是false
-- 开始循环,如果找到了数据,那么就加入循环
loop
-- 调用一次就移动一下
fetch user_cursor into user_record ;
-- 退出条件
exit when user_cursor%notfound;
DBMS_OUTPUT.put_line(user_record.id) ;
DBMS_OUTPUT.PUT_LINE(user_record.name) ;
DBMS_OUTPUT.PUT_LINE('当前行数:' || user_cursor%rowcount) ;
end loop;
-- 关闭游标
close user_cursor;
end;
-- 或
declare
-- 声明一个游标
cursor user_cursor is select id , name from T_USERS;
-- 声明一个游标类型的记录
user_record user_cursor%rowtype;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
open user_cursor;
end if;
-- user_cursor%found 在调用 fetch 之前,一直是false
-- 开始循环,如果找到了数据,那么就加入循环
loop
-- 调用一次就移动一下
fetch user_cursor into user_record ;
-- 退出条件
exit when user_cursor%notfound;
DBMS_OUTPUT.put_line(user_record.id) ;
DBMS_OUTPUT.PUT_LINE(user_record.name) ;
DBMS_OUTPUT.PUT_LINE('当前行数:' || user_cursor%rowcount) ;
end loop;
-- 关闭游标
close user_cursor;
end;
示例3:
-- 语法3
-- fetch bulk collect into 集合(常用方法) 提取所有
declare
-- 声明一个记录表
type user_record_table is table of T_USERS%rowtype index by binary_integer;
-- 声明一个游标
cursor user_cursor is select * from T_USERS;
user_record user_record_table ;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
open user_cursor;
end if;
fetch user_cursor bulk collect into user_record;
for i in 1..user_record.COUNT loop
DBMS_OUTPUT.PUT_LINE(user_record(i).ID || ' ' || user_record(i).NAME) ;
end loop;
-- 关闭游标
close user_cursor;
end;
-- fetch bulk collect into 集合(常用方法)limit 提取部分
declare
-- 声明一个记录表
type user_record_table is table of T_USERS%rowtype index by binary_integer;
-- 声明一个游标
cursor user_cursor is select * from T_USERS;
user_record user_record_table ;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
open user_cursor;
end if;
-- 只提取2条
fetch user_cursor bulk collect into user_record limit 2;
for i in 1..user_record.COUNT loop
DBMS_OUTPUT.PUT_LINE(user_record(i).ID || ' ' || user_record(i).NAME) ;
end loop;
-- 关闭游标
close user_cursor;
end;
参数游标
语法
CURSOR 游标名字( 参数名称 数据类型 ) IS 查询语句 ;
-- 定义了参数 那么在查询语句中就可以使用 游标定义的参数名称
示例
declare
-- 声明一个游标
cursor user_cursor (uname varchar)
is select id , name from T_USERS where NAME = uname;
-- 声明一个游标类型的记录
user_record user_cursor%rowtype;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
-- 在打开游标的时候 传递参数
open user_cursor('张三丰') ;
end if;
fetch user_cursor into user_record;
-- 退出条件
exit when user_cursor%notfound;
DBMS_OUTPUT.PUT_LINE(user_record.ID) ;
DBMS_OUTPUT.PUT_LINE(user_record.NAME) ;
-- 关闭游标
close user_cursor;
end;
注意:
使用游标进行更新和删除
语法
CURSOR 游标名字( 参数名称 数据类型 ) IS 查询语句 for update [of 要加锁的列] [no wait];
更新
update 表名 set xxxx where current of 游标名 ;
-- 表示在游标的当前行进行更新
declare
-- 声明一个游标
cursor user_cursor is select id , name ,WEIGHT from T_USERS for update ;
-- 声明一个游标类型的记录
user_record user_cursor%rowtype;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
-- 在打开游标的时候 传递参数
open user_cursor ;
end if;
loop
fetch user_cursor into user_record;
-- 退出条件
exit when user_cursor%notfound;
if user_record.weight < 100 then
update T_USERS set WEIGHT = WEIGHT + 100 where current of user_cursor ;
end if;
DBMS_OUTPUT.PUT_LINE(user_record.ID) ;
DBMS_OUTPUT.PUT_LINE(user_record.NAME) ;
end loop;
-- 关闭游标
close user_cursor;
end;
删除
delete from 表名 where current of 游标名 ;
-- 表示在游标的当前行进行删除
declare
-- 声明一个游标
cursor user_cursor is select id , name ,WEIGHT from T_USERS for update ;
-- 声明一个游标类型的记录
user_record user_cursor%rowtype;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
-- 在打开游标的时候 传递参数
open user_cursor ;
end if;
loop
fetch user_cursor into user_record;
-- 退出条件
exit when user_cursor%notfound;
if user_record.ID = 10 then
delete from T_USERS where current of user_cursor ;
end if;
DBMS_OUTPUT.PUT_LINE(user_record.ID) ;
DBMS_OUTPUT.PUT_LINE(user_record.NAME) ;
end loop;
-- 关闭游标
close user_cursor;
end;
使用of子句在特定的表上加 行共享锁
示例
declare
cursor user_cursor is select e.ENAME , e.JOB , d.DNAME
from EMP e ,DEPT d where e.DEPTNO = d.DEPTNO
-- 加锁的表要与更新的表一致
for update of e.EMPNO ;
-- 声明一个记录 ,记录中的字段 与游标的字段一样
user_record user_cursor%rowtype;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
-- 在打开游标的时候 传递参数
open user_cursor ;
end if;
loop
fetch user_cursor into user_record ;
-- 退出条件
exit when user_cursor%notfound;
if user_record.ENAME = 'jack' then
update EMP set ENAME = 'lucy' where current of user_cursor;
end if;
end loop;
end;
使用 nowait 子句
示例
declare
cursor user_cursor is select e.ENAME , e.JOB , d.DNAME
from EMP e ,DEPT d where e.DEPTNO = d.DEPTNO
-- 加锁的表要与更新的表一致
for update of e.EMPNO nowait ;
-- 声明一个记录 ,记录中的字段 与游标的字段一样
user_record user_cursor%rowtype;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
-- 在打开游标的时候 传递参数
open user_cursor ;
end if;
loop
fetch user_cursor into user_record ;
-- 退出条件
exit when user_cursor%notfound;
if user_record.ENAME = 'jack' then
update EMP set ENAME = 'lucy' where current of user_cursor;
end if;
end loop;
end;
游标for循环
默认会 开启 和 关闭
语法
for 定义的记录变量名 in 游标 或 一个查询语句 loop
循环内容
end loop;
使用游标for循环
示例1
declare
cursor user_cursor is select e.ENAME , e.JOB , d.DNAME
from EMP e ,DEPT d where e.DEPTNO = d.DEPTNO ;
begin
for info in user_cursor loop
DBMS_OUTPUT.PUT_LINE(info.JOB || ' ' || info.ENAME || ' ' || info.DNAME) ;
end loop;
end;
在游标for循环中使用子查询
示例2
begin
for info in ( select e.ENAME , e.JOB , d.DNAME
from EMP e ,DEPT d where e.DEPTNO = d.DEPTNO ) loop
DBMS_OUTPUT.PUT_LINE(info.JOB || ' :' || info.ENAME || ' :' || info.DNAME) ;
end loop;
end;
游标变量
游标变量使用步骤
- 定义 ref cursor 类型 和 游标变量
语法
TYPE 自定义类型名 is ref cursor [return ref cursor返回结果的类型];
游标变量名 自定义类型名;
- 打开游标
语法
open 游标变量名 for 查询语句 ;
- 提取游标数据
语法1:
fetch 游标变量 into 接收游标数据的变量;
语法2:
fetch 游标变量 bulk collect into 接收游标结果的集合变量 [limit rows ];
- 关闭游标变量
语法
close 游标变量 ;
使用游标变量
示例1
- fetch into
declare
-- 定义 ref cursor 类型
type ref_user_cursor is ref cursor ;
-- 定义游标变量
user_cursor ref_user_cursor ;
-- 声明一个记录 ,用来存放数据
type user_record is record(
e_name emp.ename%type,
e_job emp.job%type,
d_name dept.dname%type
);
info user_record ;
begin
-- 打开游标
open user_cursor for select e.ENAME , e.JOB , d.DNAME
from EMP e ,DEPT d where e.DEPTNO = d.DEPTNO ;
loop
-- 提取游标数据
fetch user_cursor into info ;
-- 退出条件
exit when user_cursor%notfound;
DBMS_OUTPUT.PUT_LINE(info.e_job || ' ' || info.e_name || ' ' || info.d_name) ;
end loop ;
close user_cursor ;
end;
示例2
- fetch bulk collect into
declare
-- 声明一个记录表 ,用来存放数据
type user_record is table of emp%rowtype index by binary_integer;
-- 定义 ref cursor 类型
type ref_user_cursor is ref cursor ;
-- 定义游标变量
user_cursor ref_user_cursor ;
info user_record ;
begin
-- 打开游标
open user_cursor for select * from EMP;
-- 提取游标数据
fetch user_cursor bulk collect into info ;
for i in 1..info.COUNT loop
DBMS_OUTPUT.PUT_LINE(info(i).ENAME || ' ' || info(i).JOB) ;
end loop;
close user_cursor ;
end;
示例3
- return子句
declare
-- 声明一个记录 ,用来存放数据
type user_record is record(
e_name emp.ename%type,
e_job emp.job%type,
d_name dept.dname%type
);
-- 定义 ref cursor 类型
type ref_user_cursor is ref cursor return user_record ;
-- 定义游标变量
user_cursor ref_user_cursor ;
info user_record ;
begin
-- 打开游标
open user_cursor for select e.ENAME , e.JOB , d.DNAME
from EMP e ,DEPT d where e.DEPTNO = d.DEPTNO ;
loop
-- 提取游标数据
fetch user_cursor into info ;
-- 退出条件
exit when user_cursor%notfound;
DBMS_OUTPUT.PUT_LINE(info.e_job || ' ' || info.e_name || ' ' || info.d_name) ;
end loop ;
close user_cursor ;
end;
使用cursor表达式
用于返回嵌套游标,通过使用cursor表达式,开发人员可以在PL/SQL块中处理更加负责的基于多张表的关联数据。为了在PL/SQL块中取得嵌套游标的数据,需要使用嵌套循环
语法
cursor(查询语句)
示例1
declare
type ref_dept_cursor is ref cursor ;
cursor dept_cursor(no number) is select d.DNAME,
cursor(select ENAME , SAL from EMP where d.DEPTNO = EMP.DEPTNO)
from DEPT d where d.DEPTNO = no;
emp_cur ref_dept_cursor;
v_ename EMP.ename%type ;
v_sal emp.sal%type ;
v_dname dept.dname%type ;
begin
open dept_cursor(&no) ;
loop
fetch dept_cursor into v_dname , emp_cur ;
exit when dept_cursor%notfound ;
DBMS_OUTPUT.PUT_LINE('部门名:' || v_dname) ;
loop
fetch emp_cur into v_ename , v_sal ;
exit when emp_cur%notfound ;
DBMS_OUTPUT.PUT_LINE('员工名:' || v_ename || '薪水:' || v_sal ) ;
end loop;
end loop;
close dept_cursor ;
end;
示例2
declare
type emp_record is record(
v_ename EMP.ename%type,
v_sal emp.sal%type
);
emp_record_instance emp_record ;
type ref_dept_cursor is ref cursor return emp_record;
cursor dept_cursor(no number) is select d.DNAME,
cursor(select ENAME , SAL from EMP where d.DEPTNO = EMP.DEPTNO)
from DEPT d where d.DEPTNO = no;
emp_cur ref_dept_cursor;
v_dname dept.dname%type ;
begin
open dept_cursor(&no) ;
loop
fetch dept_cursor into v_dname , emp_cur ;
exit when dept_cursor%notfound ;
DBMS_OUTPUT.PUT_LINE('部门名:' || v_dname) ;
loop
fetch emp_cur into emp_record_instance ;
exit when emp_cur%notfound ;
DBMS_OUTPUT.PUT_LINE('员工名:' || emp_record_instance.v_ename || '薪水:' || emp_record_instance.v_sal ) ;
end loop;
end loop;
close dept_cursor ;
end;
示例3
-- 在 游标变量中使用 游标表达式
declare
type emp_record is record(
v_ename EMP.ename%type,
v_sal emp.sal%type
);
emp_record_instance emp_record ;
type ref_dept_cursor is ref cursor return emp_record;
emp_cur ref_dept_cursor;
v_dname dept.dname%type ;
type dept_cursor is ref cursor ;
dept_cursor_instance dept_cursor ;
begin
open dept_cursor_instance for select d.DNAME,
cursor(select ENAME , SAL from EMP where d.DEPTNO = EMP.DEPTNO)
from DEPT d where d.DEPTNO = 10;
loop
fetch dept_cursor_instance into v_dname , emp_cur ;
exit when dept_cursor_instance%notfound ;
DBMS_OUTPUT.PUT_LINE('部门名:' || v_dname) ;
loop
fetch emp_cur into emp_record_instance ;
exit when emp_cur%notfound ;
DBMS_OUTPUT.PUT_LINE('员工名:' || emp_record_instance.v_ename || '薪水:' || emp_record_instance.v_sal ) ;
end loop;
end loop;
close dept_cursor_instance ;
end;
子过程
子过程 就是一个 命名的 PL/SQL 块 。 是可以保存在数据库中的。子过程 分为 : 过程 、 函数 。过程 主要是 执行 特定的操作;函数 主要是返回特定类型的数据
过程
过程用于执行特定操作
好处
- 简化开发和维护
- 提高性能
语法
create [or replace] procedure 过程名(参数 模式(in 、out 、in out) 数据类型(不带精度,宽度) , ...)
is [as] pl/sql块
示例1
-- 获取系统时间
create or replace procedure get_date
is
begin
DBMS_OUTPUT.PUT_LINE(sysdate);
end;
调用
- execute 过程名
- call 过程名()
示例
exec user_procedure;
-- 推荐
call user_procedure();
不带参数
示例2
create or replace procedure user_select_procedure
is
-- 声明一个记录表
type user_record_table is table of T_USERS%rowtype index by binary_integer;
-- 声明一个游标
cursor user_cursor is select * from T_USERS;
user_record user_record_table ;
begin
-- 如果没有打开游标,则打开游标
if not user_cursor%isopen then
open user_cursor;
end if;
fetch user_cursor bulk collect into user_record;
for i in 1..user_record.COUNT loop
DBMS_OUTPUT.PUT_LINE(user_record(i).ID || ' ' || user_record(i).NAME) ;
end loop;
-- 关闭游标
close user_cursor;
end;
带参数 in
create or replace procedure user_insert_procedure(t_id number , t_weight number , t_name varchar )
is
begin
insert into T_USERS (id , weight , name ) values ( t_id , t_weight , t_name );
end;
-- 调用 过程
call USER_INSERT_PROCEDURE(1,200,'sss') ;
-- 或
exec USER_INSERT_PROCEDURE(1,200,'sss') ;
带参数 out
create or replace procedure user_select_procedure(t_id number , t_weight out number , t_name out varchar )
is
begin
select name , weight into t_name , t_weight from T_USERS where id = t_id ;
end;
-- 调用 过程
var name varchar2(255)
var weight number
exec user_select_procedure(10 , :weight , :name)
print weight name
-- 或使用pl/sql块调用
declare
name varchar(255);
weight number ;
begin
USER_SELECT_PROCEDURE(10 , :weight , :name) ;
DBMS_OUTPUT.PUT_LINE(weight) ;
DBMS_OUTPUT.PUT_LINE(name) ;
end;
带参数 in out
同一个 参数 即负责 向 过程中传递参数,也可以从过程中获取到结果
create or replace procedure user_select_procedure(t_id in number , t_weight in out number , t_name in out varchar )
is
begin
select name , weight into t_name , t_weight from T_USERS where id = t_id
and name = t_name and weight = t_weight ;
end;
-- 调用过程
declare
name varchar(255) := 'kkk';
weight number := 200;
begin
USER_SELECT_PROCEDURE(10 , weight , name) ;
DBMS_OUTPUT.PUT_LINE(weight) ;
DBMS_OUTPUT.PUT_LINE(name) ;
end;
执行复杂的sql
示例
create or replace procedure select_by_region_name( region_name varchar2)
is
-- 声明一个 记录类型
type result_record is record(
last_name s_emp.LAST_NAME%type ,
SALARY s_emp.SALARY%type ,
dept_name s_dept.name%type ,
region_name s_region.NAME%type
) ;
-- 声明一个记录表
TYPE result_record_table IS TABLE OF result_record INDEX BY binary_integer ;
result_record_table_instance result_record_table ;
begin
select emp.LAST_NAME , emp.SALARY , dept.name , region.NAME
-- 批量绑定
bulk collect into result_record_table_instance
from s_emp emp , s_dept dept , s_region region
where emp.DEPT_ID = dept.ID and region.ID = dept.REGION_ID and region.name = region_name ;
-- 循环输出
for i in 1..result_record_table_instance.count loop
DBMS_OUTPUT.PUT_LINE( result_record_table_instance(i).last_name ) ;
end loop;
end;
过程中的参数传递
- 位置传参:默认情况,按照参数的位置一对一赋值
- 名称传参:使用名称与符合=>进行为指定参数名称进行赋值
- 混合使用:位置传参 和 名称传参 的混合使用
示例:增加用户
create procedure user_insert_procedure (
t_id t_users.id%type,
t_name t_users.name%type,
t_weight t_users.weight%type
)
is
begin
insert into T_USERS(id , name , weight ) values (t_id , t_name , t_weight);
commit;
select id , name ,weight into t_id , t_name , t_weight from T_USERS where id = t_id;
end;
- 位置传参
declare
t_id t_users.id%type := 20;
t_name t_users.name%type := 'kkk';
t_weight t_users.weight%type := 200 ;
begin
USER_SELECT_PROCEDURE( t_id , t_name , t_weight) ;
DBMS_OUTPUT.PUT_LINE(t_id) ;
DBMS_OUTPUT.PUT_LINE(t_name) ;
DBMS_OUTPUT.PUT_LINE(t_weight) ;
end;
- 名称传参
declare
t_id t_users.id%type := 20;
t_name t_users.name%type := 'kkk';
t_weight t_users.weight%type := 200 ;
begin
USER_SELECT_PROCEDURE( t_id => t_id , t_weight => t_weight , t_name => t_name) ;
DBMS_OUTPUT.PUT_LINE(t_id) ;
DBMS_OUTPUT.PUT_LINE(t_weight) ;
DBMS_OUTPUT.PUT_LINE(t_name) ;
end;
- 混合使用
declare
t_id t_users.id%type := 20;
t_name t_users.name%type := 'kkk';
t_weight t_users.weight%type := 200 ;
begin
USER_SELECT_PROCEDURE( t_id , t_weight => t_weight , t_name => t_name) ;
DBMS_OUTPUT.PUT_LINE(t_id) ;
DBMS_OUTPUT.PUT_LINE(t_weight) ;
DBMS_OUTPUT.PUT_LINE(t_name) ;
end;
删除过程
当过程不再需要的时候,用户可以使用drop procedure 命令来删除该过程
语法
drop procedure 过程名
管理子过程
- 查看源代码
可以从 user_source 中 查看 当前用户下 的 过程源代码
select text from user_source where ...;
- 查看过程
select object_name , created , status from user_objects
- 查看过程中的错误
- show errors
show errors procedure 过程名
- 使用字典user_errors
select * from user_errors;
- 重新编译过程
当子程序变成无效状态(invalid)的时候,我们可以使用命令重新编译子程序
- 编译视图
alter view 视图名 compile
- 编译过程
alter procedure 过程名 compile
- 编译方法
alter function 方法名 compile
函数
函数用于返回特定数据,函数 主要关注的是 返回值 ; 其余的 内容 与 过程 没有什么特别大的差别
语法
create [or replace] function 方法名(参数 模式(in 、out 、in out) 数据类型(不带精度,宽度) , ...)
return 函数返回类型
is [as] pl/sql块
示例
create or replace function user_select_function return varchar
is
t_name varchar(255) ;
begin
select name into t_name from T_USERS ;
return t_name ;
end;
调用
- 使用虚表
select 函数名字 from dual ;
- pl/sql块
不带任何参数
create or replace function user_select_function return number
is
t_weight number ;
begin
select weight into t_weight from T_USERS ;
return t_weight ;
end;
-- 调用
declare
t_weight number ;
begin
t_weight := user_select_function();
DBMS_OUTPUT.PUT_LINE(t_weight) ;
end;
带参数 in
create or replace function user_select_function(name varchar) return number
is
t_weight t_users.weight%type ;
begin
select weight into t_weight from T_USERS ;
return t_weight ;
end;
-- 调用
declare
t_name varchar := 'kkk' ;
t_weight t_users.weight%type ;
begin
t_weight := user_select_function(t_name);
DBMS_OUTPUT.PUT_LINE(t_weight) ;
end;
带参数 out
create or replace function user_select_function(t_id number , t_weight out number) return varchar
is
t_name t_users.name%type ;
begin
select id , name into t_id, t_name from T_USERS where t_weight = 100;
return t_name ;
end;
-- 调用
declare
t_id number := 1;
t_name t_users.name%type ;
t_weight number ;
begin
t_name := user_select_function(t_id , t_weight);
DBMS_OUTPUT.PUT_LINE(t_name) ;
end;
带参数 in out
create or replace function user_select_function(t_id in number , t_weight in out number , t_name in out varchar )return varchar
is
begin
select name , weight into t_name , t_weight from T_USERS where id = t_id
and name = t_name and weight = t_weight ;
return t_name ;
end;
-- 调用过程
declare
name varchar(255) := 'kkk';
weight number := 200;
begin
USER_SELECT_PROCEDURE(10 , weight , name) ;
DBMS_OUTPUT.PUT_LINE(weight) ;
DBMS_OUTPUT.PUT_LINE(name) ;
end;
查看函数源代码
select text from user_source where ... ;
删除函数
drop function 函数名 ;
在 PL/SQL 块中可以使用 函数或过程
示例
declare
v_result varchar2(255) ;
procedure haha is
begin
DBMS_OUTPUT.PUT_LINE('haha') ;
end;
function return_hello return varchar2 is
begin
return 'hello ! ' ;
end;
procedure do_put_line( s varchar2) is
begin
DBMS_OUTPUT.PUT_LINE(s ) ;
end;
begin
haha() ;
v_result := return_hello() ;
do_put_line( v_result ) ;
end;
包
包(Package)用于逻辑组合祥光的PL/SQL类型(record类型)、PL/SQL项(例如游标和游标变量)和PL/SQL子程序(例如过程和函数)。
通过使用PL/SQL包,不仅简化了应用设计,提高了应用性能,还可以实现信息隐藏、子程序重载等功能
包由包规范和包体两部分组成。当建立包时,需要首先建立包规范,然后再建立包体
定义包规范
包规范实际是包与应用程序之间的接口,它用于定义包的公用组件,包括常量、变量、游标、过程和函数等。
在包规范中所定义的公用组件不仅可以在包内引用,而且也可以由其他的子程序引用。如果在定义包规范中,
定义了公用变量、公用过程、公用函数,那么他们不仅可以在包内引用,而且也可以由其他子程序引用
语法
create [or replace ] package 包名 is|as
公用组件(常量 , 过程、函数、记录、记录表 、游标、游标变量 )
end 包名
示例
-- 声明包,类似于java中的接口
create or replace package user_package is
-- 声明一个常量
limit_con constant number(10) :=10 ;
-- 声明一个记录
type user_record is record(
t_id t_users.id%type,
t_name t_users.name%type,
t_weight t_users.weight%type
);
-- 声明一个记录表
type user_table is table of user_record index by binary_integer ;
-- 声明一个游标变量
type user_cursor is ref cursor;
-- 声明一个过程
procedure user_insert_procedure(t_id number , t_name varchar2 , t_weight number default limit_con) ;
-- 声明一个函数
function get_user_function( t_id number) return user_record;
function get_user_table return user_table;
end user_package ;
定义包体
包体用于实现包规范所定义的过程和函数。当建立包体时,也可以单独定义私有组件,包括变量、常量、过程和函数等,
但在包体中所定义的私有组件只能在包内使用,而不能由其他子程序引用
语法
create [or replace] package body 包名 is|as
私有的内容 以及需要实现 包规范的内容
end 包名
示例
-- 声明包主体,类似于java中的接口实现
create or replace package body user_package is
-- 声明一个记录变量
user_record_instance user_record ;
-- 声明一个记录表变量
user_table_instance user_table ;
function check_id (t_id number) return boolean
is v_temp T_USERS.id%type ;
begin
select 1 into v_temp from T_USERS where id = t_id ;
return false;
exception
when no_data_found then
return true ;
end;
procedure user_insert_procedure
(t_id number , t_name varchar2 , t_weight number default limit_con)
is
begin
if check_id(t_id) then
insert into T_USERS(id , name , weight)
values (t_id , t_name , t_weight );
else
raise_application_error(-20011,'已存在');
end if;
end ;
function get_user_function( t_id number) return user_record
is
begin
select id , name , weight into user_record_instance from T_USERS
where id = t_id ;
return user_record_instance ;
end ;
function get_user_table return user_table
is
begin
select id , name , weight
bulk collect into user_table_instance from T_USERS;
return user_table_instance ;
end ;
end user_package ;
调用包中的组件
declare
-- 声明记录表变量
user_tables user_package.user_table ;
-- 声明记录变量
user_records user_package.user_record ;
begin
-- 获取常量
DBMS_OUTPUT.PUT_LINE(user_package.limit_con) ;
-- 调用过程
user_package.user_insert_procedure(28,'GGB',200);
-- 调用函数
user_records := user_package.get_user_function(10);
DBMS_OUTPUT.PUT_LINE(user_records.t_id || user_records.T_NAME || user_records.T_WEIGHT );
DBMS_OUTPUT.PUT_LINE('----------------------');
user_tables := user_package.get_user_table();
for i in 1..user_tables.COUNT loop
DBMS_OUTPUT.PUT_LINE(user_tables(i).t_id || user_tables(i).t_name);
end loop;
end;
删除
- 删除包和包体
drop package 包名
- 删除包体
drop package body 包名