游标和触发器
一、游标
- SQL语言是面向集合的,是对指定列的操作。如果要对列中的指定行进行操作,就必须使用游标。
- 当在PL/SQL块中执行查询语句(SELECT) 和数据操纵语句(DML) 时,Oracle会为其分配一个上下文区(Context Area)
- 游标是指向上下文区的指针,它为应用提供了一种对具有多行数据查询结果集中的每一行数据分别进行单独处理的方法
显式游标
游标分为显式游标和隐含游标两种
- 隐含游标用于处理SELECT INTO和DML语句
- 显式游标则用于处理S ELECT语句返回的多行数据
- 使用显式游标
定义游标
CURSOR cursor_ name IS select statement;
打开游标
OPEN cursor name;
提取数据
FETCH cursor_name INTO variable1,variable...;
FETCH cursor_name bulk collect into collect1..;
关闭游标
CLOSE cursor name;
代码:
--创建游标 declare cursor 游标名称 is select * from 表名; 变量名 表名%rowtype; begin open 游标名;--打开游标 loop fetch 游标名 into 变量名;--提取游标 exit when 游标名%notfound; dbms_output.put_line(变量名.列名); end loop; close 游标名;--关闭游标 end;
显示游标属性
显式游标属性用于返回显式游标的执行信息
- 游标属性使用格式为: 游标名+ 属性名
- %ISOPEN
用于确定游标是否已经打开。如果游标已经打开,则返回值为TRUE; 否则返回FALSE
- %FOUND
检查是否从结果集中提取到数据。如果提取到数据,则返回值为TRUE; 否则返回FALSE
- %NOTFOUND
与%FOUND属性恰好相反,如果提取到数据,则返回值为FALSE; 否则返回TRUE
- %ROWCOUNT
返回到当前行为止已经提取到的实际行数
参数游标
参数游标是指带有参数的游标。在定义了参数游标之后,使用不同参数值多次打开游标可以生成不同的结果集。
代码:
--参数游标 declare cursor cursor_wxn(x_empno number)--定义参数(形参) is select * from emp where empno=x_empno; v_name emp%rowtype; begin open cursor_wxn(7369);--(实参) loop fetch cursor_wxn into v_name; exit when cursor_wxn%notfound; dbms_output.put_line(v_name.ename); end loop; close cursor_wxn; end;
游标for循环
游标for循环是在pl/sql块中使用游标的最简单方式,它可以简化对游标的处理。当使用游标for循环时,oracle会隐含的打开游标,提取游标数据并关闭游标。
代码:
--游标for循环 declare v_name emp%rowtype; cursor cursor_wxn is select * from emp; begin for v_name in cursor_wxn loop dbms_output.put_line(v_name.ename); end loop; end;
上面代码中无须进行取值和关闭的操作,游标for循环可以自己进行。
下面还有一个更为简单的游标for循环,参考上面的代码进行读阅:
--简单for begin for v_name in (select * from emp) loop dbms_output.put_line(v_name.ename); end loop; end;
使用游标变量
代码实例:
declare type youbiao_bianliang is ref cursor;--变量类型 cursor_varisble youbiao_bianliang;--定义游标类型的变量 v_name emp%rowtype;--变量 begin open cursor_varisble for select * from emp where empno=7788; loop fetch cursor_varisble into v_name; exit when cursor_varisble%notfound; dbms_output.put_line(v_name.ename); end loop; close cursor_varisble; end;
隐含游标
作用:用属性进行一些判断(一种判断方式)
含义:当执行一条DML语句或者SELECT...INTO语句时,都会创建一个隐含游标。
隐含游标的名称是SQL,不能对SQL游标显示执行OPEN、FETCH和CLOSE语句。
Oracle隐式地打开、提取,并总是自动地关闭SQL游标。
属性:
- SQL%FOUND:只有DML语句影响一行或多行时,SQL%FOUND属性才返回true。
- SQL%NOTDOUND:如果DNL语句没有影响行数,此属性将返回false。
- SQL%ROWCOUNT:返回DML影响的行数,如果DML语句没有影响行则返回0.
- SQL%ISOPEN:此属性用于判断SQL游标是否已经打开。在执行SQL语句之后,Oracle自动关闭SQL游标,所以隐含游标的SQL%ISOPEN属性始终为false。
简单示例1:
-- 隐含游标 begin delete from emp where empno=1; if sql%notfound then dbms_output.put_line('找不到记录'); else dbms_output.put_line('记录'); end if; end;
示例2:
declare a number:=1; begin delete from emp where empno=a; if sql%notfound then dbms_output.put_line('没有改变'); else dbms_output.put_line('改变'); end if; end;
二、触发器
- 触发器组成
1、触发事件
DML或DDL语句。
2、触发时间
是在触发事件发生之前(before) 还是之后(after) 触发
3、触发操作
使用PL/SQL块进行相应的数据库操作
4、触发对象
表、视图、模式、数据库
5、触发频率
触发器内定义的动作被执行的次数,包括语句级和行级ji。
- 限制
1、触发器不接受参数
2、一个表上最多可有12个触发器,但同一时间、同一事件、同一类型的触发器只能有一个。并各触发器之间不能有矛盾。
3、一个表上的触发器越多,该表上的DM操作的性能影响就越大
4、触发器代码的大小不能超过32K。如需要大量的代码创建触发器,则首先创建过程,然后在触发器中使用CALL语句调用过程
5、触发器代码只能包含SELECT、INSERT、UPDATE和DELETE语句,
6、不能包含DDL语句(CREATE、ALTER和DROP) 和事务控制语句(COMMIT、ROLLBACK和SAVEPOINT)
- 创建触发器
语句触发器
1、语句触发器是指当执行DML语句时被隐含执行的触发器
2、如果在表上针对某种DML操作创建了语句触发器,则当执行DML操作时会自动地执行触发器的相应代码
3、为了审计DML操作,或者确保DML操作安全执行时,可以使用语句触发器
触发器用途很多,例如用户清算购物车后将会触发待收货的数据库
代码示例:
--创建触发器 create or replace trigger tri_test before--触发之前 update or delete--更新或删除 on emp for each row--对行进行操作 begin dbms_output.put_line(:old.sal);--old表示数据库旧值 insert into demo(id) values (:new.sal);--new新值 end; update emp set sal=888 where empno=7788; commit; --代码解释:先执行创建触发器代码后,再执行最后的更新语句。当更新恩平、表后将会输出数据库中本来存放的值,并且触发添加语句在demo表中插入一条语句。
自定义异常,触发器 拦截
简单代码示例1:
-- 触发器 拦截 create or replace trigger tri_test before update or delete on emp for each row begin if to_char(sysdate,'yyyy-mm-dd')='2018-03-13' then--将系统日期转化为字符类型 -- 自定义异常, raise_application_error(-20000,'今天不能修改数据');-- 负20000之前的异常都已经被定义 end if; end; update emp set sal=777 where empno=7788; commit; --触发器创建后,执行最后代码,将会有异常提示:今天不能修改数据
示例2:
create or replace trigger tri_test before update or delete or insert on emp for each row begin case when updating then raise_application_error(-20000,'今天不能修改'); when inserting then raise_application_error(-20001,'今天不能插入'); when deleting then raise_application_error(-20000,'今天不能删除'); end case; end; update emp set sal=777 where empno=7788; insert into emp (empno) values (123); commit;