PLSQL触发器,游标

时间:2023-03-09 01:55:42
PLSQL触发器,游标
 --触发器
drop table emp_log
create table emp_log(
empno number,
log_date date,
new_salary number,
action varchar2(20) --动作记录
);
create or replace trigger log_sal_adj
after update of sal on emp --指定当update执行后,监控对emp表sal列的更改 before or adter
for each row --每update一行执行一次触发器代码
declare
v_action varchar2(20); --定义一个保存更新行为的字符串变量
begin
if :old.sal >:new.sal THEN
v_action :='减少工资';
elsif :old.sal<:new.sal then
v_action :='增加工资';
end if;
insert into emp_log
(empno,log_date,new_salary,action)
values
(:new.empno,sysdate,:new.sal,v_action);
end;
select * from emp_log;
select sal from emp where empno=7369;
update emp set sal=8000 where empno=7369;
--异常处理示例
declare
v_result number:=0;
v_dividend number:=&a; --定义替换变量
begin
v_result:=round(1000/v_dividend,2);
dbms_output.put_line('结果值为:'||v_result);
exception
when zero_divide then --如果是被0除错误
dbms_output.put_line('出现被0除的错误了!');
when others then --所有其他错误的异常处理句柄
dbms_output.put_line(sqlcode); --输出错误代码
dbms_output.put_line(sqlerrm); --输出错误消息
end;
--记录类型示例
Declare
type emp_rec_type is record(
empno emp.empno%type,
ename varchar2(50),
job varchar2(20) );
emp_info_rec emp_rec_type;
emp_row_rec emp%Rowtype; --=>(eno emp.empno%type +enme emp.ename%type)
begin
emp_info_rec.empno:=8222;
emp_info_rec.ename:='李三思';
emp_info_rec.job :='销售人员';
insert into emp (empno,ename,job)
values (emp_info_rec.empno,emp_info_rec.ename,emp_info_rec.job);
select * into emp_row_rec from emp where empno=8222;
dbms_output.put_line('新插入的员工记录信息:'||CHR(10)||'工号:'||
emp_row_rec.empno||CHR(10)||'姓名:'||
emp_row_rec.ename||CHR(10)||'职位:'||
emp_row_rec.job);
end; --集合类型使用示例
declare
--定义保存员工工号的索引表,其类型为emp表中的empno字段相同的类型
type NumTab is table of emp.empno%type index by binary_integer; --pls_integer
type NameTab is table of emp.ename%type index by binary_integer;
--定义集合类型的变量
enums NumTab;
names NameTab; /* 定义块 type语句定义了NumTab和NameTab这两个索引表,
is table of指定索引的类型, index by指定索引的下标数据类型
binary_integer是PL/SQL中的整数类型 匿名块print_first_n,它的作用范围仅限于匿名块内部
执行 select..bulk collect into将表中查询出来的多行记录写入到集合中
*/
procedure print_first_n(n POSITIVE) is
BEGIN
IF enums.count =0 then
DBMS_OUTPUT.put_line('当前集合为空!');
else
DBMS_OUTPUT.put_line('前'||n||'名员工:');
for i in 1..n loop
dbms_output.put_line('员工工号:'||enums(i)||': '||names(i));
end loop;
end if;
END; begin
select empno,ename bulk collect
into enums,names from emp
order by empno;
print_first_n(3);
print_first_n(6);
end; select empno,ename from emp order by empno; --使用游标遍历结果集
declare
emprow emp%rowtype;
cursor emp_cur
is
select * from emp where deptno=20;
begin
open emp_cur;
loop
fetch emp_cur
into emprow;
DBMS_OUTPUT.put_line('员工编号:'||emprow.empno||' '
||'员工名称:'||emprow.ename);
exit when emp_cur%NOTFOUND;
end loop;
close emp_cur; --关闭游标
end; 1 emprow是一个记录类型,保存fetch语句提取的记录值
2 cursor is 指向一个返回结果集的select语句
3 open语句打开游标,指向结果集内存区域
4 游标使用完之后,必须显示调用close关闭游标,释放资源 子程序不像匿名块仅仅是一次使用,子程序存储在数据字典中
可以被其它子程序重复调用 --使用游标和索引表显示员工名称
DECLARE
--定义员工名称索引表
TYPE emp_table IS TABLE OF VARCHAR2(10);
emplist emp_table; --定义表类型的变量
CURSOR empcursor IS
SELECT ename FROM emp;
BEGIN
IF NOT empcursor%ISOPEN THEN
OPEN empcursor;
END IF;
FETCH empcursor BULK COLLECT
INTO emplist;
FOR i IN 1 .. emplist.COUNT LOOP
DBMS_OUTPUT.put_line('员工名称:' || emplist(i));
END LOOP;
CLOSE empcursor;
END;
--rowid使用示例
declare
v_empname rowid;
v_other varchar2(18);
begin
select rowid into v_empname from emp where empno=&empno;
dbms_output.put_line(v_empname);
v_other:=rowidtochar(v_empname);
dbms_output.put_line(v_other);
end; select rowid from emp where empno=7369; --批量提取游标数据
DECLARE
type depttab_type is table of dept%rowtype;
depttab depttab_type;
cursor deptcur is select * from dept;
begin
open deptcur;
fetch deptcur bulk collect into depttab; for i in 1 .. depttab.count
loop
dbms_output.put_line(depttab (i).deptno ||' '||depttab(i).dname||' '||depttab(i).loc);
end loop;
close deptcur;
end; --操纵游标数据 loop循环
declare
dept_row dept%rowtype;
cursor dept_cursor is select * from dept;
begin
open dept_cursor;
loop
fetch dept_cursor into dept_row;
exit when dept_cursor%notfound;
dbms_output.put_line('部门名称:'||dept_row.dname);
end loop;
close dept_cursor;
end; --while循环
declare
dept_row dept%rowtype;
cursor dept_cursor is select * from dept;
begin
open dept_cursor;
fetch dept_cursor into dept_row;
while dept_cursor%found loop
dbms_output.put_line('部门名称:'||dept_row.dname);
fetch dept_cursor into dept_row;
end loop;
close dept_cursor;
end;
调用了2次fetch语句, 判断之前fetch一次获取%found属性值
后面的fetch语句在循环体内对每一次的循环求值 /*游标for循环 尽管定义为一个显式游标,但PLSQL引擎进行特别处理
不需要open,close;*/
declare
cursor dept_cursor is select * from dept;
begin
for dept_row in dept_cursor loop
dbms_output.put_line('部门名称:'||dept_row.dname);
end loop;
end; --游标FOR循环子查询语句
begin
for dept_row in (select * from dept) loop
dbms_output.put_line('部门名称:'||dept_row.dname);
end loop;
end; --使用游标更新数据
declare
cursor emp_cursor(p_deptno in number)
is select * from emp where deptno=p_deptno for update;
--使用for update子句添加互斥锁
begin
for emp_row in emp_cursor(20)
loop
update emp set sal=sal*1.12
where current of emp_cursor;
end loop;
commit;
end; select sal,ename from emp where deptno=20; --使用游标删除数据
declare
cursor emp_cursor(p_empno in number)
is select * from emp_copy where empno=p_empno for update;
begin
for emp_row in emp_cursor(7369)
loop
delete from emp_copy where current of emp_cursor;
end loop;
end; --游标变量
declare
type emp_type is ref cursor return emp%rowtype;
emp_cur emp_type;
emP_row emp%rowtype;
begin
open emp_cur for select * from emp;
loop
fetch emp_cur into emp_row;
exit when emp_cur%notfound;
dbms_output.put_line('员工名称:'|| emp_row.ename);
end loop;
end; 11.14
--使用close 语句关闭游标变量
--如果type语句中未指定return子句,则可以连续地打开多次,分别为其赋不同的select语句
declare
type emp_type is ref cursor return emp%rowtype;
emp_cur emp_type;
emp_row emp%rowtype;
begin
open emp_cur for select * from emp where deptno=20;
fetch emp_cur into emp_row;
while emp_cur%found loop
dbms_output.put_line('员工名称:'||emp_row.ename);
fetch emp_cur into emp_row;
end loop;
close emp_cur;
end; 关闭一个还没有打开过的游标变量或已经关闭了的游标变量是非法的
PLSQL引发invalid_cursor异常
declare
type emp_curtype is ref cursor;
emp_cur1 emp_curtype;
emp_cur2 emp_curtype;
emp_row emp%rowtype;
begin
open emp_cur1 for select * from emp where deptno=20;
fetch emp_cur1 into emp_row;
dbms_output.put_line('员工名称:'||emp_row.ename||'部门编号:'
||emp_row.deptno);
fetch emp_cur2 into emp_row;
exception
when invalid_cursor then
emp_cur2:=emp_cur1;
fetch emp_cur2 into emp_row;
dbms_output.put_line('员工名称:' ||emp_row.ename||
'部门编号:'||emp_row.deptno);
--重新打开emp_cur2游标变量,利用相同的查询区域
open emp_cur2 for select * from emp where deptno=40;
fetch emp_cur1 into emp_row;
--emp_cur1与emp_cur2共享相同的查询区域,因此结果相同
dbms_output.put_line('员工名称:' ||emp_row.ename||
'部门编号:'||emp_row.deptno);
end; 使用open for语句打开emp_cur2时候,将相同的查询区域执行另一个查询语句
由于emp_cur1与emp_cur2都指向相同的查询区域,提取emp_cur1时候
实际相当于对emp_cur2提取
--处理ROWTYPE_MISMATCH异常
declare
type emp_curtype is ref cursor;
emp_cur emp_curtype;
emp_row emp%rowtype;
dept_row dept%rowtype;
begin
open emp_cur for select * from emp where deptno=20;
fetch emp_cur into dept_row;
exception
when rowtype_mismatch then
fetch emp_cur into emp_row;
dbms_output.put_line('员工名称:'||emp_row.ename
||'部门编号:'||emp_row.deptno);
end;
--使用sys_refcursor类型 不需要type语句显式定义弱类型游标
declare
emp_cur sys_refcursor; --定义弱类型游标变量
emp_row emp%rowtype;
dept_row dept%rowtype;
begin
open emp_cur for select * from emp where deptno=20;
fetch emp_cur into dept_row;
exception
when rowtype_mismatch then
fetch emp_cur into emp_row;
dbms_output.put_line('员工名称:'||emp_row.ename
||'部门编号:'||emp_row.deptno);
end;
--在包中使用游标变量
create or replace package emp_data_action as
type emp_type is ref cursor return emp%rowtype; --定义强类型游标
procedure getempbydeptno(emp_cur in out emp_type,p_deptno number);
end emp_data_action; create or replace package body emp_data_action as
procedure getempbydeptno(emp_cur in out emp_type,p_deptno number)
is emp_row emp%rowtype;
begin
open emp_cur for select * from emp where deptno=p_deptno;
loop
fetch emp_cur into emp_row;
exit when emp_cur%notfound;
dbms_output.put_line('员工名称:'||emp_row.ename
||'部门编号:'||emp_row.deptno);
end loop;
close emp_cur;
end;
end emp_data_action; declare
emp_cursors emp_data_action.emp_type;
begin
emp_data_action.getempbydeptno(emp_cursors,20);
end; 游标变量的限制
1.不能在包中声明游标变量
2 不能在创建表或创建视图的语句中把字段类型指定为REF CURSOR类型
数据库字段不能存放游标变量值
3 游标类型参数不支持远程过程调用
4 不能将ref cursor 类型作为集合的元素类型
5 不能在游标中使用游标for循环
一个事务必须满足ACID 即原子性,一致性,隔离性和持久性
--使用保存点局部回滚
declare
dept_no number(2) :=90;
begin
savepoint A;
insert into dept values(dept_no,'市场部','北京');
savepoint B;
insert into emp values(7997,'威尔','销售人员',null,trunc(sysdate),5000,300,dept_no);
savepoint c;
insert into dept values(dept_no,'后勤部','上海'); --插入相同编号的部门记录
commit;
exception
when dup_val_on_index then
dbms_output.put_line(sqlerrm);
rollback to b;
end; select * from dept;
--控制触发顺序 FOLLOWS子句
create or replace trigger one_trigger
before insert on trigger_data for EACH ROW
begin
:new.trigger_id := :new.trigger_id+1;
DBMS_OUTPUT.put_line('触发了one_trigger');
end; create or replace trigger two_trigger
before insert on trigger_data
for each row follows one_trigger --让该触发器在one_trigger后面触发
begin
DBMS_OUTPUT.put_line('触发了two_trigger');
if :new.trigger_id>1
then
:new.trigger_id := :new.trigger_id +2:
end if;
end; --系统触发器 scott
create table created_log
(
obj_owner varchar2(30),
obj_name varchar2(30),
obj_type varchar2(20),
obj_user varchar2(30),
created_date DATE
)
--system
create or replace trigger t_created_log
after create on scott.SCHEMA --在Scott方案下创建对象后触发
begin
insert into scott.created_log(obj_owner,obj_name,obj_type,obj_user,
created_date) values (sys.dictionary_obj_owner,sys.dictionary_obj_name,
sys.dictionary_obj_type,sys.login_user,sysdate);
end;
--scott
create table temp_table(field1 varchar2(20),field2 number(5));
select * from created_log;
触发器属性列表 图6
--ora_is_drop_column和ora_is_alter_column 禁止非法更改列
create or replace trigger preserve_app_cols
after alter on schema
declare
cursor curs_get_columns (cp_owner varchar2,cp_table varchar2)
is select column_name from all_tab_columns
where owner = cp_owner and table_name=cp_table;
begin
if ora_dict_obj_type ='TABLE'
THEN
FOR v_column_rec in curs_get_columns (ora_dict_obj_owner,ora_dict_obj_name)
loop
if ora_is_drop_column(v_column_rec.column_name)
then
if v_column_rec.column_name='EMPNO' THEN
RAISE_APPLICATION_ERROR(-2003,'不能对empno字段进行修改');
end if;
end if;
end loop;
end if;
end;
alter table emp drop column empno
select * from emp;