0
点赞
收藏
分享

微信扫一扫

PL/SQL

茗越 2022-05-03 阅读 72

PL/SQL

利用SQL进行编程

PL/SQL基础

PL/SQL块结构

PL/SQL块,由三部分组成

  1. 定义部分:用于定义常量、变量、游标、例外、复杂数据类型等
  2. 处理部分:用于实现应用模块功能,该部分包含了要执行的PL/SQL语句和SQL语句
  3. 例外部分:用于处理执行部分可能出现的运行错误

基本结构

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、BLOBCLOBNCLOB
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 ;
  1. 简单条件判断
    用于执行单一条件判断,如果满足特定条件,则会执行相应操作;如果不满足条件,则退出条件分支语句。简单条件判断是使用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;
  1. 二重条件分支
    二重条件分支是根据条件来选择两种可能性,当使用二重条件分支的时候,如果满足条件,则执行一组操作;如果不满足条件,则执行另外一组操作。二重条件分支是利用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;
  1. 多重条件分支
    用于执行最复杂的条件分支操作,当使用多重条件分支时,如果满足第一个条件,则执行第一种操作,如果不满足第一个条件,则检查是否满足第二个条件,如果满足第二个条件,则执行第二种操作;如果不满足第二个条件,则检查是否满足第三个条件,以此类推。多重条件分时是使用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;
  1. 在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 ;
  1. 在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块中引入例外处理部分,可以捕捉到各种例外,并根据例外出现的情况进行相应的处理

例外分类

  1. 预定义例外:定义好的例外,类似于java中已经定义好的1异常,比如IndexOutOfBoundsException
  2. 非预定义例外:非预定义例外用于处理与预定义里无关的Oracle错误,就是不知道的错误
  3. 自定义例外:指由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 操作中使用 记录
  1. 在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;
  1. 在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;
  1. 在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_intergerPLS_INTEGERvarchar2
类似于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的两种游标类型:

使用显示游标的步骤:

  1. 声明游标:cursor 游标的名字 is 一个查询语句 ;
  2. 打开游标:open 游标的名字 ;
  3. 提取游标:使用fetch 进行提取数据 ;

    语法分类:

    1. 提取数据到 一个变量 中,一般是用来 处理没有关系 的数据:fetch 游标名字 into 变量名 ;
    2. 提取数据到 一个记录 中,一般是用来 处理有关系的 数据:fetch 游标名字 into 记录名 ;
    3. 提取数据到 集合 中,形式:fetch 游标名字 bulk collect into 集合 [limit rows] ;
  4. 关闭游标: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 过程名(参数 模式(inoutin 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 过程名
管理子过程
  1. 查看源代码
    可以从 user_source 中 查看 当前用户下 的 过程源代码
select text from user_source where ...;
  1. 查看过程
select object_name  , created , status from user_objects 
  1. 查看过程中的错误
  • show errors
show errors procedure 过程名
  • 使用字典user_errors
select * from user_errors;
  1. 重新编译过程
    当子程序变成无效状态(invalid)的时候,我们可以使用命令重新编译子程序
  • 编译视图
alter view 视图名 compile
  • 编译过程
alter procedure 过程名 compile
  • 编译方法
alter function 方法名 compile

函数

函数用于返回特定数据,函数 主要关注的是 返回值 ; 其余的 内容 与 过程 没有什么特别大的差别
语法

create [or replace] function 方法名(参数 模式(inoutin 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 包名
举报

相关推荐

0 条评论