PL/SQL

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、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 ;
  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_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的两种游标类型:

使用显示游标的步骤:

  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 过程名(参数 模式(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 过程名
管理子过程
  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 方法名(参数 模式(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 包名

触发器

触发器是指存放在数据库中,并被隐含执行的存储过程。在Oralce中,允许基于表或视图的DML操作建立触发器;
也允许基于系统事件(启动数据库、关闭数据库、登录)和DDL建立触发器。
触发器由触发事件、触发条件和触发操作三部分组成。

  • 触发事件
    触发事件是指引起触发器被触发的SQL语句、数据库事件或用户事件。在Oracle中,触发事件不仅仅可以是DML操作,也可以是其他操作
    举例:
    • 启动和关闭例程
    • Oracle错误信息
    • 用户登录与断开会话
    • 特定表或视图的DML操作
    • 在任何方案上的DDL语句
  • 触发条件
    触发条件是可选操作,一般是指使用when子句指定一个boolean表达式,当boolean表达式返回值为true时,会自动执行触发器相应代码;
    当boolean表达式返回值为false或unknown时,不会执行触发操作
  • 触发操作
    触发操作是指包含SQL语句和其他执行代码的PL/SQL块,当触发条件为true时,会自动执行触发操作的相应代码。
    注意:
    • 一般 触发操作不会超过 32K , 如果有大量代码, 则需要使用 过程 进行封装 ; 然后在触发操作中调用
    • 触发操作一般只包含 CRUD 操作。
DML触发器

在建立了DML触发器之后,如果发生了相应的DML操作,就会自动执行触发器的相应代码。
当建立了DML触发器时,需要指定触发时机( BEFORE 或 AFTER )、触发事件( INSERT , UPDATE , DELETE)、表名、触发类型、触发条件及触发操作。

  • 触发时机:用于指定触发器的触发时间,before 或 after 表示在具体的操作之前 或 之后
  • 触发事件:用于指定导致触发器执行的DML操作,即insert、update、delete 操作
  • 表名:用于指定哪张表,即操作哪一张表的时候会触发
  • 触发类型:用于指定当触发事件发生之后,需要执行几次触发操作。
  • 触发条件:用于指定执行触发器代码的条件,只有条件为true时才会执行触发器代码。注意:当编写DML触发器时,只允许在行触发器上指定触发条件。
  • 触发操作:用于指定触发器执行代码。
语句触发器

指当执行DML语句时被隐含执行的触发器。
语法

  create [or replace] trigger 触发器名字
  timing event [or event2 or event3]
  on 表名
  pl/sql块

说明

  • time:触发时机(before 或 after)
  • event:触发事件
    条件谓词
    针对于指定的操作进行判定
  • inserting:当触发事件时INSERT操作时,该条件谓词返回值为TRUE,否则为FALSE
  • updating:当触发事件时UPDATE操作时,该条件谓词返回值为TRUE,否则为FALSE
  • deleting:当触发事件时DELETE操作时,该条件谓词返回值为TRUE,否则为FALSE

举例涉及的表结构

-- 创建一个日志表
create table user_log(
    id number(11) primary key ,
    operator_time timestamp ,
    operator_user varchar2(255) ,
    table_name varchar2(255) ,
    operator_name varchar2(255)
);

-- 创建序列
create sequence user_log_ID
    maxvalue 9999999
    nocache

具体示例
示例1:before语句触发器

-- 当t_users表在执行insert、update、delete操作之前对user_log进行insert操作
create or replace TRIGGER user_trigger
before delete or insert or update
on T_USERS
declare
begin
    insert into user_log values (user_log_ID.nextval ,
                                 systimestamp , 'aa' , 'T_USERS' , 'insert');
end;

示例2:after语句触发器并指定条件谓词

-- 当t_users表在执行insert、update、delete操作之后对user_log进行insert操作,且操作名字随着操作的不同而不同
create or replace TRIGGER user_trigger
after delete or insert or update
on T_USERS
declare
    operator_names varchar2(255) ;
begin
    case
        when inserting then operator_names := 'insert' ;
        when updating  then operator_names := 'update' ;
        when deleting  then operator_names := 'delete' ;
    end case ;
    insert into user_log values (user_log_ID.nextval ,
                                 systimestamp , 'aa' , 'T_USERS' , operator_names );
end;
行触发器

行触发器是指执行DML操作时,每作用一行就触发一次的触发器。
语法

create [or replace] trigger 触发器名字
  timing event [or event2 or event3]
  on 表名
  [reference OLD as old | NEW as new]
  for each row
  [when condition]
  pl/sql块

说明

  • time:触发时机(before 或 after)
  • event:触发事件
  • reference:REFERENCING子句用于指定引用新、旧数据的方式,默认情况下使用old修饰符引用旧数据,使用new修饰符引用新数据
  • for each row:表示建立行触发器
  • when condition:用于指定触发条件

举例涉及的表结构

-- 创建一个统计每种操作数量的表
create table user_operator_log(
                         id number(11) primary key ,
                         operator_time timestamp ,
                         operator_user varchar2(255) ,
                         table_name varchar2(255) ,
                         insert_count number(11),
                         update_count number(11),
                         delete_count number(11),
                         select_count number(11)
);

-- 创建序列
create sequence user_operator_lod_ID
    maxvalue 9999999
    nocache

-- 插入一条数据
insert into user_operator_log values (user_operator_lod_ID.nextval ,
                                      systimestamp , 'aa' , 'T_USERS' ,
                                      0 , 0 , 0 ,0
                                     );

具体示例
after行触发器

-- 当T_USERS表进行insert、update、delete操作时,对应的count+1
create or replace Trigger user_operator_count
    after insert or update or delete
    on T_USERS
    for each row
declare
begin
    case
        when inserting then update user_operator_log
                            set operator_time = systimestamp,
                                operator_user = 'c##root',
                                insert_count  = insert_count + 1
                            where table_name = 'T_USERS';
        when updating then update user_operator_log
                           set operator_time = systimestamp,
                               operator_user = 'c##root',
                               update_count  = update_count + 1
                           where table_name = 'T_USERS';
        when deleting then update user_operator_log
                           set operator_time = systimestamp,
                               operator_user = 'c##root',
                               delete_count  = delete_count + 1
                           where table_name = 'T_USERS';
        end case ;
end;

系统事件触发器
常用事件属性函数
  • ora_client_ip_address:返回客户端IP地址
  • ora_database_name:返回当前数据库名
  • ora_des_encrypted_password:用于返回DES加密后的用户口令
  • ora_dict_obj_name:用于返回DDL操作所对应的数据库对象名
  • ora_dict_obj_list(name_list out ora_name_list_t):返回在事件中被修改的对象名列表
  • ora_dict_obj_owner:返回DDL操作所对应的对象的所有者名
  • ora_dict_obj_owner_list(owner_list out ora_name_list_t):返回在事件中被修改对象的所有者列表
  • ora_dict_obj_type:返回DDL操作所对应的数据库对象的类型
  • ora_grantee(user_list out ora_name_list_t):返回授权事件的授权者
  • ora_instance_num:返回例程号
  • ora_is_alter_column(column_name in varchar2):用于检测特定列是否被修改
  • ora_is_creating_nested_table:用于检测是否正在建立嵌套表
  • ora_is_drop_column(column_name in varchar2):用于检测特定列是否被删除
  • ora_is_servererror(error_number):用于检测是否返回特定oracle错误
  • ora_login_user:返回登录用户名
  • ora_sysevent:用于返回触发触发器的系统事件名
建立例程启动和关闭触发器

创建事件表

create table event_table(
    event varchar2(30) , 
    time date 
);
  • 例程启动触发器
create or replace trigger tr_startup
    after startup
    on database
begin
    insert into event_table values (ora_sysevent, sysdate);
end;
  • 例程关闭触发器
create or replace trigger tr_shutdown
    before shutdown
    on database
begin
    insert into event_table values (ora_sysevent, sysdate);
end;
建立登录和退出触发器

创建日志表

create table log_table(    
    username varchar2(20) ,    
    logon_time date ,   
    logoff_time date ,     
    address varchar2(20) 
); 
  • 登录触发器
create or replace trigger tr_logon
    after logon
    on database
begin
    insert into log_table(username, logon_time, address)
    values (ora_login_user, sysdate, ora_client_ip_address);
end;
  • 退出触发器
create or replace trigger tr_logoff
    before logoff
    on database
begin
    insert into log_table(username, logoff_time, address) 
    values (ora_login_user, sysdate, ora_client_ip_address);
end;
instead of 触发器

对于简单视图,可以直接执行INSERT , UPDATE 和DELETE操作。但是对于复杂视图,不允许直接执行INSERT , UPDATE 和 DELETE 操作。
当视图符合以下任何一种情况时,都不允许直接执行DML操作。
具体情况如下:

  • 具有集合操作符( UNION , UNION ALL , INTERSECT , MINUS )
  • 具有分组函数( MIN , MAX , SUM , AVG , COUNT等 )
  • 具有GROUP BY , CONNECT BY 或 START WITH等子句
  • 具有DISTINCT关键字;具有连接查询
    为了在具有以上情况的复杂视图上执行DML操作,必须要基于视图建立INSTEAD-OF触发器。
    建立INSTEAD-OF触发器注意事项:
  • INSTEAD OF 选项只适用于视图
  • 当基于视图建立触发器时,不能指定BEFORE和AFTER选项
  • 在建立视图时没有指定WITH CHECK OPTION选项
  • 当建立INSTEAD-OF触发器是,必须指定FOR EACH ROW 选项

创建视图dept_emp

create or replace view dept_emp as
    select d.DEPTNO as d_id, d.DNAME as d_name, 
           e.EMPNO as e_id, e.ENAME as e_name from dept d , emp e 
    where d.DEPTNO = e.DEPTNO ;

语法

create or replace trigger 触发器名称 instead of 操作 on 视图
for each row
PL/SQL块

示例

create or replace TRIGGER dept_emp_trigger instead of insert on dept_emp
    for each row
declare
    dept_count number(11) ;
    emp_count number(11) ;
begin
    insert into user_log values (user_log_ID.nextval ,
                                 systimestamp , 'c##root' , 'dept_emp' , 'insert');
    select count(*) into dept_count from dept where dept.DEPTNO = :new.d_id ;
    if dept_count = 0 then
        insert into dept(deptno , dname) values (:new.d_id , :new.d_name) ;
    end if;

    select count(*) into emp_count from emp where emp.EMPNO = :new.e_id ;
    if emp_count = 0 then
        insert into emp(empno , ename) values (:new.e_id , :new.e_name) ;
    end if;
end;

建立一个DDL触发器:必须使用after关键字

create or replace trigger trigger_user_ddl
    after DDL
    on c##root.SCHEMA
declare
    operator_name_date varchar2(255 char) := to_char(ora_login_user) || '-' || to_char(ora_dict_obj_name) || '-' ||
                                             to_char(ora_dict_obj_type) ;
begin
    insert into user_log
    values (user_log_ID.nextval,
            systimestamp, 'c##root', 'T_USERS', operator_name_date);
end;
管理触发器
  • 显示触发器信息
select 触发器名字 , status from user_triggers ;
  • 禁止触发器
alter trigger 触发器名字 disable ;
  • 激活触发器
alter trigger 触发器名字 enable ;
  • 禁止所有触发器
alter table 表名 disable all triggers ;
  • 激活所有触发器
alter table 表名 enable all triggers ;
  • 重新编译
alter trigger 触发器名字 compile ;
  • 删除触发器
drop trigger 触发器名 ;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值