PL/SQL学习
Posted listslim1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PL/SQL学习相关的知识,希望对你有一定的参考价值。
PL/SQL基础
SQL分类
数据查询语言DQL,基本结构是由SELECT子句,FROM子句,WHERE子句组成的查询块。
数据操纵语言DML,主要有三种形式:
1) 插入:INSERT
2) 更新:UPDATE
3) 删除:DELETE
数据定义语言DDL,用来创建数据库中的各种对象。如:
CREATE TABLE/VIEW/INDEX/SYN/CLUSTER(表/视图/索引/同义词/簇)
数据控制语言DCL,用来授予或回收访问数据库的某种特权,并控制数据库操纵事务 发生的时间及效果,对数据库实行监视等。如:
1) GRANT:授权。
2)ROLLBACK [WORK] TO [SAVEPOINT]:回退到某一点。
3) COMMIT [WORK]:提交。
事物提交
在数据库的插入、删除和修改操作时,只有当事务在提交到数据库时才算完成。在事务提交前,只有操作数据库的这个人才能有权看到所做的事情,别人只有在最后提交完成后才可以看到。提交数据有三种类型:显式提交、隐式提交及自动提交。1) 显式提交
用COMMIT命令直接完成的提交为显式提交。
2) 隐式提交
用SQL命令间接完成的提交为隐式提交。这些命令是:ALTER,AUDIT,COMMENT,CONNECT,CREATE,DISCONNECT,DROP,EXIT,GRANT,NOAUDIT,QUIT,REVOKE,RENAME。
3) 自动提交
若把AUTOCOMMIT设置为ON,则在插入、修改、删除语句执行后,系统将自动进行提交,这就是自动提交。其格式为:SET AUTOCOMMIT ON;
数据表的基本操作
Create table user(id number primary key, name varchar2(20)), sex varchar2(1) default ‘男’;
2) 数据类型
数字型:number(p,s)和float
字符型:varchar2, nvarchar2, char, nchar, long
日期型:date和timestamp
其他数据类型:blob(二进制) clob(字符串) bfile(文件)
3) 约束
约束分为:主键约束、外键约束、唯一约束、检查约束和非空约束
Primary key
Constraint fk_puublish foreign key(publish) references publishinfo (publishid)
Constraint un_bookname unique (bookname)
Constraint ck_price check (price>10 and price<100)
Not null
4)修改表
Alter table bookinfo add ISBN varchar2 (20); //增加列
Alter table bookinfo modify ISBN varchar2(80) not null; //修改列
Alter table bookinfo drop column ISBN; //删除列
Alter table bookinfo add constraint pk_bookinfo primary key (bookid); //增加主键约束
Alter table bookinfo drop pk_bookinfo; //删除被创建的主键约束
5)删除表
Drop table user;
Drop,truncate和delete区别:delete是逐行删除数据,可回滚;truncate是一次删除全部数据,不可回滚;drop是删除表。
6)重命名表
Rename user to people;
表中数据的基本操作
1)查询数据Select bookname, author from bookinfo;
2)添加数据
Insert into books(id, name, pubdate, publish) values (1001,’数字信号处理’, to_date(‘2016.3.6’, ‘yyyy.mm.dd’), default);
Insert into books (id, name, publish, price, pubdate) select id, name, publish, price, pubdate from bookinfo;
Insert select语句通常用于创建查找表,以提高检索性能。查找表可以包含分布在多个数据库的多个表中的数据。因为多个表连接处理起来比简单查询要慢,对一个查找表执行select查询,则明显快于执行又长又复杂的连接查询。另一个用途是备份表。备份将要删除,截断数据或重新装入数据的表。
3)修改数据
Update books set name=’随机信号处理’ where id=1001;
4)删除数据
Delete from books where publish=’大众出版社’;
Truncate table books;
数据的基本查询
1)Select语句语法Select [distinct | all] select_list from table_list [where_clause] [group_by_clause] [having condition] [coder_by_clause]
2)执行顺序
Step1: 首先执行from子句,根据来自不同数据源的数据执行cross join运算创建工作表。
Step2: 如果有where子句,将where子句中的条件作用于step1中生成的工作表,删除那些不满足条件的行;
Step3: 如果有group by子句,它将step2中生成的结果表中的行分成多个组,(通常与聚合函数一起使用),DBMS将每组减少到单行,而后将其添加到新的结果表中,用以代替step1的工作表。
Step4: 如果有having子句,它将筛选分组。删除不满足having子句条件的行
Step5: 将select子句作用于结果表,删除结果表中不包含在select_list中的列。如果select中包含distinct关键词,DBMS将从结果中删除重复的行。
Step6: 如果有order by子句,则按指定的顺序对结果进行排序。
Step7: 对于交互式select语句,在屏幕上显示结果;对于嵌入式的SQL,使用游标将结果传递给宿主程序。
3)Where子句
=, >, <, !=, <=, >= eg: where publish=’大众出版社’ and price>50
AND, OR, NOT eg: where not publish=’大众出版社’
LIKE(_和%通配符) eg: where book like ‘%/%test%’ ESCAPE ‘/’
BETWEEN AND eg: where price between 35 and 60
IN eg: where reader in (1000,2000,3000)
4)Having子句
A)如果指定了group by子句,那么having子句定义搜索条件将作用于这个group by
B)如果指定where子句,而没有group by,那么having作用于where子句的输出
C)如果既没有group by,也没有where,那么having作用于from子句的输出
5)Order by子句
ASC, DESC 默认升序(ASC)排列 eg: order by reader desc
Null值位置 eg: order by reader desc nulls last
多列排序 eg: order by reader desc, pubdate asc
6)运算符
算数运算符:+ - * /
比较运算符:=, >, <, !=, <=, >=
逻辑运算符:AND OR NOT BETWEEN IN LIKE ALL ANY SOME EXISTS
字符串连接:||
赋值:declare v_name varchar2(10); begin v_name := ‘test’; end;
查询中函数的使用
1)字符串处理函数Length() eg: select job, length(job) from emp;
Trim() eg: select job, trim(leading|trailing|both ‘s’ from job) trim_job from emp;
Substr() eg: select job, substr(job, 1, 3) from emp;
Concat() eg: select name, concat(‘test’, name) from emp;
Upper()和lower() eg: select name, lower(name) from emp;
Instr() eg: select name instr(name, ‘test’) from emp;
2)数值处理函数
Abs() eg: select abs(-5) from dual;
Mod() eg: select mode(9, 2) from dual;
sin() eg: select sin(1.57) from dual;
Round() eg: select round(3.65) from dual; //返回4
Trunc() eg: select trunc(321.45678, 4), trunc(1095.1, -2) from dual; //返回321.4567 1000
3)日期处理函数
Sysdate() eg: select sysdate, to_char(sysdate, ‘yyyy-mm-dd’) from dual;
Systimestamp() eg: select systimestamp from dual;
转换函数
Cast() eg: select cast(sysdate as varchar2(18)) test1, cast(‘234’ as number(5) test2) from dual;
To_char() eg:select to_char(3.156, ‘9.99’) from dual; //输出3.16
To_date() eg: select to_date(‘2016-03-06’, ‘yyyy-mm-dd’) from dual;
To_number() eg: select to_number(‘89.2’, ‘99.9’) from dual; // 输出89.2
4)Null相关函数
Nvl() eg: select name, nvl(name, ‘test’) from bookinfo;
Lnnvl() eg: select I from emp where lnnvl(name is null); //where条件表示name不为null。
5)聚合函数
Sum()和Avg() select sum(ALL | DISTINCT price) as sum_price from bookinfo;
Max()和Min() select max(ALL | DISTINCT price) as max_price from bookinfo;
Count() select count(nvl(to_char(reader), ‘test’)) as reader_count from bookinfo;
Stddev()
Stddev_pop()
数据表的高级查询
多表查询
Select t1.id, t1.name, t2.id, t2.name from t1, t2 where t1.id=t2.id; //笛卡尔积后判断where条件如果不需要输出t2的信息,可以采用select子句:
Select t1,id, t1.name from t1 where t1.id in (select t2.id from t2);
JOIN连接
内连接:等值连接:计算笛卡尔积Select t1.id, t1.name, t2.id, t2.name from t1 join t2 on t1.id=t2.id; //等价于
select t1.id, t1.name, t2.id, t2.name from t1,t2 where t1.id=t2.id;
自然连接:根据字段名自然连接
Select t1.id, t1.name, t2.id, t2.name from t1 natural join t2;
外连接:select * from t1 LEFT | RIGHT | FULL join t2 on t1.id
交叉连接:select * from t1CROSS join t2; //等价于select * from t1, t2 没有on子句
如果表t1有m条记录,t2有n条记录,则交叉连接后有m*n条记录。
集合运算Union,MINUS与INTERSECT
对于Union,所有选择列表中的表达式数据必须相同,对应列的数据类型必须相同,eg:
Select id, name, unit, bookcount from readerinfo where unit=’计算机系’ UNION
Select id, name, unit, bookcount from readerinfo where bookcount>=2
子查询
子查询是一个嵌套在select,insert,update或者delete语句或者其他子查询中的查询。1) 非相关子查询,子查询可以独立运行。Eg:
Select id, name, unit, bookcount from readerinfo where id in (select distinct reader from bookinfo where reader is not null) order by bookcount;
2) 相关子查询,首先执行外部查询,对外部查询的每一行分别执行一次子查询。Eg:
Select id, name, unit, bookcount from readerinfo r where id in (select distinct reader from bookinfo b where b.reader = r.id) order by bookcount;
3) 子查询中的聚合函数
Select id, name, publish, price from bookinfo where price > (select avg(price) from bookinfo) order by price;
4) 子查询中使用exists
Select id, name, publish, price, reader from bookinfo b where not exists (select * from readerinfo r where (r.unit=’计算机系’ ) AND r.id=b.reader) AND reader is not null order by reader;
5) 使用exists子查询实现集合交操作
Select * from t1 where exists (select * from t2 where t1.id=t2.id);
Select * from t1 where id = any(select id from t2); // any实现
6) 使用exists检查表中的重复行
Select id, name, publish, price, reader from bookinfo b where exists(select name, count(*) from bookinfo where name=b.name group by name having count(*)>1) oder by name;
7) select中使用子查询
Select id, name, sex, unit, bookcount, (select count(*) from bookinfo where reader=readerinfo.id) as borrowcount from readerinfo oder by borrowcount;
8) having子句中使用子查询
Select unit, sum(bookcount) as canborrows from readerinfo r where unit is not null group by unit having (sum(bookcount)-2) > (select count(*) from bookinfo b where reader in (select id from readerinfo r2 where r2.unit = r.unit)) order by canborrows;
9) DML中使用子查询
Update bookinfo B set remarks=’可续借’ where (select bookcount from readerinfo where id=b.reader) > (select count(*) from bookinfo b2 where b2.reader=b.reader);
IN/EXISTS/ANY/ALL比较
Exists:子查询至少返回一行时条件为true。Not Exists:子查询不返回任何一行时条件为true。
In:与子查询返回结果集中某个值相等。
Not In:与子查询返回结果集中任何一个值不相等。
>ANY:比子查询返回结果中的某个值大。
=ANY:与子查询返回结果中的某个值相等。
<ANY:比子查询返回结果中的某个值小。
>ALL:比子查询返回结果中的所有值都大。
<ALL :比子查询返回结果中的所有值都小。
PL/SQL高级技术
索引、视图和序列
索引索引可以分为:唯一索引,B-tree索引。。。。创建唯一索引:
Create unique index uq_ind_readerid on readerinfo(readerid ASC|DESC);
如果对表中的某列定义了unique约束,则自动为该了创建唯一索引。
在查询过程中,如果创建的索引没有被默认使用,可以采用强制使用索引的方式来使用已经创建的索引。Eg:
Select /*+ index (readerinfo uq_ind_readerid) */ * from readerinfo;
创建复合索引
Create index ind_readerid_bokcot on readerinfo(readerid, bookcount);
查看索引
Select * from user_index where table_name=’bookinfo’;
重命名索引
Alter index ind_readerid_bokcot rename to ind_readerid_bokcot_re;
重建索引
Alter index ind_reader_bokcot_re rebuild;
删除索引
Drop index ind_readerid_bokcot_re;
创建单表视图
Create view readerinfo_view as select id, name, sex, unit, bookcount from readerinfo where unit=’计算机系’ with read only; // with read only设置只读
创建多表视图
Create view reader_books as select r.id, r.name, r.unit, r.bookcount, b.id, b.name from readerinfo r inner join bookinfo b on r.id=b.reader;
修改视图内容
Create or replace view readerinfo_view as select id, name, sex, unit, bookcount from readerinfo where unit=’自动化系’ with check option
删除视图
Drop view readerinfo_view;
创建序列
Create sequence cus_read;
查看指定序列信息
Select * from user_sequence where sequence_name=’sequence_name’;
Select * from user_objects where object_name=’object_name’;
使用序列
Select cus_read.currval from dual;
Insert into readerinf values(cus_read.nextval, ‘test’, ‘test’); //当使用insert语句插入数据并rollback之后,创建的序列并不会回滚;
序列初始值,步长,最小值和最大值
Create sequence cus_read start with 1;
Alter sequence cus_read increment by 2;
Alter sequence cus_read minvalue 100;
Alter sequence cus_read nomaxvalue;
数据类型、流程控制和游标
数据类型按是否常量可以分为:常量与变量
V_mypro varchar2(20) := ‘我的变量’;
V_mypro varchar2(20) not null default ‘我的变量’;
V_myproth constant varchar2(20) := ‘我的常量’;
按类型分为4种:标量、复合、引用和LOB。
1) 标量类型有NUMBER, VARCHAR2, TRUE, FALSE, NULL, DATE, TIMESTAMP,
%TYPE。Eg:
Declare v_id t2.id%type;
begin
select id into v_id from t2;
end;
2) 复合类型有自定义RECORD, %ROWTYPE, IS TABLE OF(索引表类型), VARRAY(变长数组)。Eg: RECORD example
Declare
Type readerinfo_rc is record
(
V_readerid number(4);
V_readername varchar2(10);
);
V_readerinfo_rc readerinfo_rc;
Begin
Select readerid, readername into v_readerinfo_rc from readerinfo where readerid=1000;
End;
%ROWTYPE example
Declare
V_readerinfo_rc readerinfo%rowtype;
Begin
Select readerid, readername into v_readerinfo_rc from readerinfo where readerid=1000;
End;
索引表类型example
Declare
TYPE binary_inx_fst is table of readerinfo%rowtype index by binary_integer;
V_inx_bnary binary_inx_fst;
Begin
Select * into v_inx_bnary(1) from readerinfo where readerid=9701;
End;
VARRAY example
Declare
TYPE varray is varray(50) of varchar2(10);
V_varr varray := varray(‘1’, ‘2’, ‘3’);
Begin
V_varr(1) := ‘我是’;
V_varr(2) := ‘变长’;
V_varr(3) := ‘数组’;
End;
1) IF...ELSE流程
If v_value > 40 then
Dbms_output.put_line(v_value || ‘大于40’);
Else if v_value <10 then
Dbms_output.put_line(v_value || ‘小于10’);
Else
Dbms_output.put_line(v_value || ‘大于10,小于40’);
End if;
2) CASE流程
Select id, name, unit, bookcount,
( case
When bookcount>3 then ‘高’
When bookcount<1 then ‘低’
When bookcount>=1 and bookcount<=3 then ‘中’
End) as readerlevel
From readerinfo
Order by readerid;
3) LOOP流程
//利用when语句退出循环
<<fst_loop>>
Loop
V_rlt := v_rlt+1;
Exit fst_loop when v_rlt>3;
End loop;
//利用if语句退出循环
<<fst_loop>>
Loop
V_rlt := v_rlt+1;
If v_rlt > 3 then
Exit fst_loop;
End if;
End loop fst_loop;
// while loop结构
<<while_loop>>
While v_rlt<4 Loop
V_rlt := v_rlt+1;
End loop while_loop;
// for loop结构
<<for_loop>>
For v_rlt in 1..5 Loop
V_rlt := v_rlt+1;
End loop while_loop;
1) 显示游标逐行取数据处理
Decalre
Cursor book_cusor is
Select * from bookinfo where publish in(‘大众出版社’, ‘青年出版社’) order by price;
Cur_book_cusor bookinfo%rowtype;
Begin
Open book_cusor;
Loop
Fetch book_cusor into cur_book_cusor;
Exit when book_cusor %NOTFOUND;
End Loop;
Close book_cusor;
End;
2) 显示游标批量取数据处理
Decalre
Cursor book_cusor is
Select * from bookinfo where publish in(‘大众出版社’, ‘青年出版社’) order by price;
Type bookinfo_tab is table of bookinfo%rowtype;
Cur_book_cusor bookinfo_tab;
Begin
Open book_cusor;
Loop
Fetch book_cusor bulk collect into cur_book_cusor limit 3; // limit 3表示每次取3条
For i in 1..cur_book_cusor.count Loop
Dbms_output.put_line(cur_book_cusor(i).bookid);
End Loop;
Exit when book_cusor %NOTFOUND;
End Loop;
Close book_cusor;
End;
3) 隐式游标(查询或者DML语句自动打开一个隐式游标)
Decalre
Cur_book bookinfo%rowtype;
Begin
Select * into cur_book where readerid=1000; //自动打开隐式游标,默认名称为SQL
If SQL%ISOPEN then
Dbms_output.put_line(‘isopen is true’);
Else
Dbms_output.put_line(‘isopen is true’);
End if;
If SQL%ISFOUND then
Dbms_output.put_line(‘%FOUND is true’);
End if;
Update readerinfo set sex=’女’ where sex=’男’;
Dbms_output.put_line(‘update语句影响的记录数:’ || SQL%ROWCOUNT);
ROLLBACK;
EXCEPTION
When NO_DATA_FOUND then
Dbms_output.put_line(SQL%ROWCOUNT);
Dbms_output.put_line(‘没有数据!’);
End;
存储过程与函数
1) 执行,编译和删除存储过程
exec procedure_name(paraeters);
Alter procedure pro_name compile;
Drop procedure pro_name;
如果存储过程中有输出语句,那么需要设置serveroutput的输出状态,set serveroutput on;
2) 创建无参数存储过程
Create or replace procedure pro_reader
as
begin
update readerinfo set bookcount=bookcount+1 where unit=‘计算机系‘;
end;
3) 在存储过程中使用游标
create or replace procedure pro_reader
as
v_readerinfo readerinfo%rowtype;
cursor cursor_readerinfo
is
select * from readerinfo where unit in (‘自动化系‘,‘计算机系‘) order by unit;
begin
open cursor_readerinfo
Loop
fetch cursor_readerinfo into v_readerinfo;
exit when cursor_readerinfo%NOTFOUND;;
if v_readerinfo.bookcount<3 then
update readerinfo set bookcount=bookcount+1 where id=v_readerinfo.id;
end if;
end loop;
close cursor_readerinfo
end;
4) 在存储过程中创建表
使用存储过程,有事为了方便可以再存储过程中创建表,用于存储临时数据。在存储过程中使用DDL或者动态执行语句,需要使用execute immediate来完成。在存储过程中创建临时表需要为用户授权:grant create any table to <user>; execute immediate语句在编程过程中应尽量少用,过量使用会影响代码运行效率。
create or replace procedure pro_reader
as
tabeexists varchar2(2);
my_createtab varchar2(400);
begin
select count(1) into tebeexists from all_tables where table_name=‘my_test_tab‘;
my_createtab :=‘create global temporary table my_test_tab(test varchar2(20) not null) on commit proserve rows‘;
if tebeexists=0 then
execute immedate my_createtab;
else
execute immedate ‘delete from my_test_tab‘;
end if;
end;
5) 带有输入参数的存储过程
create or replace procedure pro_readerlist(unit in varchar2, minbookcount in number default 2)
as
type reader_book_rc is record(
v_id readerinfo.id%type,
v_name readerinfo.name%type,
v_unit readerinfo.unit%type,
v_bookcount readerinfo.bookcount%type,
v_count_reader number(8)
);
v_reader_book_rc reader_book_rc;
cursor cur_readerbook
is
select r.id, r.name, r.unit, r.bookcount, count(b.reader) borrowedbooks
from readerinfo r left join bookinfo b on r.id=b.reader
where r.unit like unit and r.bookcount>=minbookcount
group by r.id, r.name, r.unit, r.bookcount order by r.bookcount;
begin
open cur_readerbook;
Loop
fetch cur_readerbook into v_reader_book_rc;
exit when cur_readerbook%NOTFOUND;
dbms_output.put_line(v_reader_book_rc.v_id...);
end loop;
close cur_readerbook;
end
6) 带有输出参数的存储过程
crerate procedure pro_out_elt(parm_out out varchar2)
as
begin
parm_out := ‘计算机系‘;
end;
调用pro_out_elt
create or replace procedure pro_reader_cotse
as
v_readerinfo readerinf%rowtype;
v_elmts varchar2(30);
cursor cursor_readerinfo
is
select * from readerinfo where unit=v_elmts order by unit;
begin
pro_out_elt(v_elmts);
open cursor_readerinfo;
Loop
fetch cursor_readerinfo into v_readerinfo;
exit when cursor_readerinfo%NOTFOUND;
if v_readerinfo.bookcount<3 then
update readerinfo set bookcount=bookcount+1 where id=v_readerinfo.id;
dbms_output.put_line(‘数据表修改完毕...‘);
end if;
end loop;
close cursor_readerinfo;
end;
1)调用与删除函数
SQL> var title varchar2(100);
SQL> var result number;
SQL> exec :result := get_user(‘test’, title);
SQL> print result title;
SQL> drop function get_user
2)创建函数
Create or replace function get_user(name IN varchar2, title out varchar2)
Return number
Is
V_age usr.age%TYPE;
Begin
Select u.age, u.title into v_age, title from users u where upper(u.name)=upper(name);
Return v_user;
end
触发器
一个表上最多可有12个触发器,但同一时间、同一事件、同一类型的触发器只能有一个。并各触发器之间不能有矛盾。
在一个表上的触发器越多,对在该表上的DML操作的性能影响就越大。
触发器最大为32KB。若确实需要,可以先建立过程,然后在触发器中用CALL语句进行调用。
在触发器的执行部分只能用DML语句(SELECT、INSERT、UPDATE、DELETE),不能使用DDL语句(CREATE、ALTER、DROP)。
触发器中不能包含事务控制语句(COMMIT,ROLLBACK,SAVEPOINT)。因为触发器是触发语句的一部分,触发语句被提交、回退时,触发器也被提交、回退了。
在触发器主体中调用的任何过程、函数,都不能使用事务控制语句。
在触发器主体中不能申明任何Long和blob变量。新值new和旧值old也不能向表中的任何long和blob列。
不同类型的触发器(如DML触发器、INSTEAD OF触发器、系统触发器)的语法格式和作用有较大区别。
触发器类型
1)DML触发器ORACLE可以在DML语句进行触发,可以在DML操作前或操作后进行触发,并且可以对每个行或语句操作上进行触发。
2)Instead of触发器
由于在ORACLE里,不能直接对由两个以上的表建立的视图进行操作。所以给出了替代触发器。它就是ORACLE 8专门为进行视图操作的一种处理方法。
3)系统触发器
ORACLE 8i 提供了第三种类型的触发器叫系统触发器。它可以在ORACLE数据库系统的事件中进行触发,如ORACLE系统的启动与关闭等。
创建触发器
1)创建DML触发器。CREATE TABLE emp_his AS SELECT * FROM EMP WHERE 1=2; CREATE OR REPLACE TRIGGER tr_del_emp BEFORE DELETE --指定触发时机为删除操作前触发 ON scott.emp FOR EACH ROW --说明创建的是行级触发器 BEGIN --将修改前数据插入到日志记录表 del_emp ,以供监督使用。 INSERT INTO emp_his(deptno , empno, ename , job ,mgr , sal , comm , hiredate ) VALUES( :old.deptno, :old.empno, :old.ename , :old.job,:old.mgr, :old.sal, :old.comm, :old.hiredate ); END; DELETE emp WHERE empno=7788; DROP TABLE emp_his; DROP TRIGGER del_emp;限制对Departments表修改(包括INSERT,DELETE,UPDATE)的时间范围,即不允许在非工作时间修改departments表。
CREATE OR REPLACE TRIGGER tr_dept_time BEFORE INSERT OR DELETE OR UPDATE ON departments BEGIN IF (TO_CHAR(sysdate,'DAY') IN ('星期六', '星期日')) OR (TO_CHAR(sysdate, 'HH24:MI') NOT BETWEEN '08:30' AND '18:00') THEN RAISE_APPLICATION_ERROR(-20001, '不是上班时间,不能修改departments表'); END IF; END;
限定只对部门号为80的记录进行行触发器操作。
CREATE OR REPLACE TRIGGER tr_emp_sal_comm BEFORE UPDATE OF salary, commission_pct OR DELETE ON HR.employees FOR EACH ROW WHEN (old.department_id = 80) BEGIN CASE WHEN UPDATING ('salary') THEN IF :NEW.salary < :old.salary THEN RAISE_APPLICATION_ERROR(-20001, '部门80的人员的工资不能降'); END IF; WHEN UPDATING ('commission_pct') THEN IF :NEW.commission_pct < :old.commission_pct THEN RAISE_APPLICATION_ERROR(-20002, '部门80的人员的奖金不能降'); END IF; WHEN DELETING THEN RAISE_APPLICATION_ERROR(-20003, '不能删除部门80的人员记录'); END CASE; END; /* 实例: UPDATE employees SET salary = 8000 WHERE employee_id = 177; DELETE FROM employees WHERE employee_id in (177,170); */
利用行触发器实现级联更新。在修改了主表regions中的region_id之后(AFTER),级联的、自动的更新子表countries表中原来在该地区的国家的region_id。
CREATE OR REPLACE TRIGGER tr_reg_cou AFTER update OF region_id ON regions FOR EACH ROW BEGIN DBMS_OUTPUT.PUT_LINE('旧的region_id值是'||:old.region_id ||'、新的region_id值是'||:new.region_id); UPDATE countries SET region_id = :new.region_id WHERE region_id = :old.region_id; END;
在触发器中调用过程。
CREATE OR REPLACE PROCEDURE add_job_history ( p_emp_id job_history.employee_id%type, p_start_date job_history.start_date%type, p_end_date job_history.end_date%type, p_job_id job_history.job_id%type, p_department_id job_history.department_id%type ) IS BEGIN INSERT INTO job_history (employee_id, start_date, end_date, job_id, department_id) VALUES(p_emp_id, p_start_date, p_end_date, p_job_id, p_department_id); END add_job_history; --创建触发器调用存储过程... CREATE OR REPLACE TRIGGER update_job_history AFTER UPDATE OF job_id, department_id ON employees FOR EACH ROW BEGIN add_job_history(:old.employee_id, :old.hire_date, sysdate, :old.job_id, :old.department_id); END;
创建instead of触发器
INSTEAD OF 选项使ORACLE激活触发器,而不执行触发事件。只能对视图和对象视图建立INSTEAD OF触发器,而不能对表、模式和数据库建立INSTEAD OF 触发器。
只能被创建在视图上,并且该视图没有指定WITH CHECK OPTION选项。
不能指定BEFORE 或 AFTER选项。
FOR EACH ROW子可是可选的,即INSTEAD OF触发器只能在行级上触发、或只能是行级触发器,没有必要指定。
没有必要在针对一个表的视图上创建INSTEAD OF触发器,只要创建DML触发器就可以了。
创建delete替代触发器
CREATE OR REPLACE VIEW emp_view AS SELECT deptno, count(*) total_employeer, sum(sal) total_salary from emp group by deptno;在此视图中直接删除是非法的:
SQL> delete from emp_view where deptno=10;
ORA-01732: 此视图的数据操纵操作非法
但是我们可以创建INSTEAD_OF触发器来为 DELETE 操作执行所需的处理,即删除EMP表中所有基准行:
CREATE OR REPLACE TRIGGER emp_view_delete INSTEAD OF DELETE ON emp_view FOR EACH ROW BEGIN DELETE FROM emp WHERE deptno= :old.deptno; END emp_view_delete; DELETE FROM emp_view WHERE deptno=10; DROP TRIGGER emp_view_delete; DROP VIEW emp_view;
创建复杂视图,针对insert创建instead of触发器,向视图插入数据
CREATE OR REPLACE FORCE VIEW "HR"."V_REG_COU" ("R_ID", "R_NAME", "C_ID", "C_NAME") AS SELECT r.region_id, r.region_name, c.country_id, c.country_name FROM regions r, countries c WHERE r.region_id = c.region_id;
CREATE OR REPLACE TRIGGER "HR"."TR_I_O_REG_COU" INSTEAD OF INSERT ON v_reg_cou FOR EACH ROW DECLARE v_count NUMBER; BEGIN SELECT COUNT(*) INTO v_count FROM regions WHERE region_id = :new.r_id; IF v_count = 0 THEN INSERT INTO regions (region_id, region_name) VALUES (:new.r_id, :new.r_name); END IF; SELECT COUNT(*) INTO v_count FROM countries WHERE country_id = :new.c_id; IF v_count = 0 THEN INSERT INTO countries(country_id, country_name, region_id) VALUES (:new.c_id, :new.c_name, :new.r_id); END IF; END;
创建系统时间触发器
CREATE TABLE log_event (user_name VARCHAR2(10), address VARCHAR2(20), logon_date timestamp, logoff_date timestamp); --创建登录触发器 CREATE OR REPLACE TRIGGER tr_logon AFTER LOGON ON DATABASE BEGIN INSERT INTO log_event (user_name, address, logon_date) VALUES (ora_login_user, ora_client_ip_address, systimestamp); END tr_logon; --创建退出触发器 CREATE OR REPLACE TRIGGER tr_logoff BEFORE LOGOFF ON DATABASE BEGIN INSERT INTO log_event (user_name, address, logoff_date) VALUES (ora_login_user, ora_client_ip_address, systimestamp); END tr_logoff;
异常处理
异常情况处理(EXCEPTION)是用来处理正常执行过程中未预料的事件,程序块的异常处理预定义的错误和自定义错误,由于PL/SQL程序块一旦产生异常而没有指出如何处理时,程序就会自动终止整个程序运行。有三种类型的异常错误:
1. 预定义 ( Predefined )错误
ORACLE预定义的异常情况大约有24个。对这种异常情况的处理,无需在程序中定义,由ORACLE自动将其引发。
2. 非预定义 ( Predefined )错误
即其他标准的ORACLE错误。对这种异常情况的处理,需要用户在程序中定义,然后由ORACLE自动将其引发。
3. 用户定义(User_define) 错误
程序执行过程中,出现编程人员认为的非正常情况。对这种异常情况的处理,需要用户在程序中定义,然后显式地在程序中将其引发。
预定义 ( Predefined )错误
例:更新指定员工工资,如工资小于1500,则加100;
DECLARE v_empno employees.employee_id%TYPE := &empno; v_sal employees.salary%TYPE; BEGIN SELECT salary INTO v_sal FROM employees WHERE employee_id = v_empno; IF v_sal<=1500 THEN UPDATE employees SET salary = salary + 100 WHERE employee_id=v_empno; DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'员工工资已更新!'); ELSE DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'员工工资已经超过规定值!'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('数据库中没有编码为'||v_empno||'的员工'); WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('程序运行错误!请使用游标'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END;
非预定义 ( Predefined )错误
例:删除指定部门的记录信息,以确保该部门没有员工。
INSERT INTO departments VALUES(50, 'FINANCE', 'CHICAGO'); DECLARE v_deptno departments.department_id%TYPE := &deptno; deptno_remaining EXCEPTION; PRAGMA EXCEPTION_INIT(deptno_remaining, -2292); /* -2292 是违反一致性约束的错误代码 */ BEGIN DELETE FROM departments WHERE department_id = v_deptno; EXCEPTION WHEN deptno_remaining THEN DBMS_OUTPUT.PUT_LINE('违反数据完整性约束!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END;
用户定义(User_define) 错误
例:更新指定员工工资,增加100;
DECLARE v_empno employees.employee_id%TYPE :=&empno; no_result EXCEPTION; BEGIN UPDATE employees SET salary = salary+100 WHERE employee_id = v_empno; IF SQL%NOTFOUND THEN RAISE no_result; END IF; EXCEPTION WHEN no_result THEN DBMS_OUTPUT.PUT_LINE('你的数据更新语句失败了!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM); END;
性能与安全
待续..以上是关于PL/SQL学习的主要内容,如果未能解决你的问题,请参考以下文章