视图:是一个封装了各种复杂查询的语句,就称为视图。不存储数据,只存储定义,定义被保存在数据字典中
作用:1、可以保证安全,隐藏一些数据,保证数据不会被误删;
2、多表连接,可以使复杂的查询易于理解和使用
15.1、创建视图
需要权限才能创建
grant create view to scott;
CREATE OR REPLACE VIEW 视图名字(字段) AS 子查
CREATE OR REPLACE VIEW v_表名_业务 AS 查询语句
建立一个只包含 20 部门雇员信息的视图(雇员的编号、姓名、工资)
CREATE VIEW empv20 (empno,ename,sal) AS SELECT empno,ename,sal FROM emp WHERE deptno=20 ;
现在直接更新视图里的数据 将 7369 的部门编号修改为 30。此操作在视图中完成。
update empv20 SET deptno=30 where empno=7369 ; 此时,提示更新完成。
默认情况下创建的视图,如果更新了,则会自动将此数据从视图中删除,之后会更新原本的数据。
在建立视图的时候有两个参数:
· WITH CHECK OPTION à 保护视图的创建规则
CREATE OR REPLACE VIEW empv20
AS SELECT empno,ename,sal,deptno FROM emp WHERE deptno=20
WITH CHECK OPTION CONSTRAINT empv20_ck;—-约束条件
ALTER TABLE emp1 add CONSTRAINT empv20_ck check(约束条件);
再执行更新操作:
update empv20 SET deptno=30 where empno=7369 ; à 此处更新的是部门编号,失败
|- 之前是按照部门编号建立的视图,所以不能修改部门编号
update empv20 SET ename=’tom’ where empno=7369 ; à 可以更新,更新的是名字,成功
· WITH READ ONLY(只读,不可修改),视图最好不要轻易的修改
CREATE OR REPLACE VIEW empv20
AS SELECT empno,ename,sal,deptno FROM emp WHERE deptno=20
WITH READ ONLY;
现在任意的字段都不可更改,所以现在的视图是只读的。
如果视图的基表有多行查询(比如:group by,distinct)那么该视图也是只读的
15.1、查看视图
SELECT * FROM v_emp;
update v_emp SET deptno=20 where empno=7369;
DELETE FROM v_emp WHERE empno=7369;
ROLLBACK;
DROP VIEW v_emp;
在视图终不能被修改删除,1、多表构成的视图;2、group by
资料:
http://database.51cto.com/art/200904/118306.htm
http://blog.csdn.net/fan_xiao_ming/article/details/6174065
工作原理:rowid
用执行计划检测索引是否起作用(set autot on exp)
执行计划 cbo(选择)和cro(规则)
16.1、索引
索引是一种用于提升查询效率的数据库对象,通过快速定位数据的方法,索引信息与表独立存放,Oracle数据库自动使用和维护索引
索引的存储
索引和表都是独立存在的。在为索引指定表空间的时候,不要将被索引的表和索引指向同一个表空间,这样可以避免产生 IO 冲突。使 Oracle 能够并行访问存放在不同硬盘中的索引数据和表数据,更好的提高查询速度。
16.2、索引优缺点
建立索引的优点
1.加快数据的检索速度;
2.创建唯一性索引,保证数据库表中每一行数据的唯一性;
3.加速表和表之间的连接;
4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
索引的缺点
1.索引需要占物理空间。
2.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。
16.3、创建索引
创建索引的方式
1、自动创建:在定义主键或唯一键约束时系统会自动在相应的字段上创建唯一性索引
2、手动创建:用户可以在其他列上创建非唯一索引,以加速查询。
创建索引:创建索引一般有以下两个目的:维护被索引列的唯一性和提供快速访问表中数据的策略。
–在 select 操作占大部分的表上创建索引;
–在 where 子句中出现最频繁的列上创建索引;
–在选择性高的列上创建索引(补充索引选择性,最高是 1,eg:primary key)
–复合索引的主列应该是最有选择性的和 where 限定条件最常用的列,并以此类推第二列……。
–小于 5M 的表,最好不要使用索引来查询,表越小,越适合用全表扫描。
Create [UNIQUE|BITMAP] index
[schema.]index_name on [schema.]table_name(column_name[ASC|DESC],…n,[column_expression])|CLUSTER [schema.]cluster_name
[INITRANS integer]
[MAXTRANS integer]
[PCTFREE integer]
[PCTUESD integer]
[TABLESPACE tablespace_name]
[STORAGE storage_clause]
[NOSORT]
[REVERSE]
UNIQUE 指定索引所基于的列(或多列)值必须唯一。默认的索引是非唯一的。
BITMAP 指定建立位映射索引而不是B*索引。位映射索引保存的行标识符与作为位映射的键值有关。位映射中的每一位都对应于一个可能的行标识符,位设置意味着具有对应行标识符的行包含该键值。
ON table_name 建立基于函数的索引。用table_name的列、常数、SQL函数和自定义函数创建的表达式。指定column_expression,以后用基于函数的索引查询时,必须保证查询该column_expression不为空。
CLUSTER 创建cluster_name簇索引。若表不用schema限制,oracle假设簇包含在你自己的方案中。不能为散列簇创建簇索引。
NOSORT 数据库中的行以升序保存,在创建索引时不必对行排序。若索引列或多列的行不以升序保存,oracle会返回错误。
REVERSE 指定以反序索引块的字节,不包含行标识符。NOSORT不能与REVERSE一起指定。
CREATE INDEX idx_表名_列名 on 表名(列1,列2…)
create index abc on student(sid,sname);
create index abc1 on student(sname,sid);
这两种索引方式是不一样的
索引 abc 对 Select * from student where sid=1; 这样的查询语句更有效索引 abc1 对 Select * from student where sname=?louis?; 这样的查询语句更有效
因此建立索引的时候,字段的组合顺序是非常重要的。一般情况下,需要经常访问的字段放在组合字段的前面
16.4、使用索引的原则
–查询结果是所有数据行的 5%以下时,使用 index 查询效果最好;
–where 条件中经常用到表的多列时,使用复合索引效果会好于几个单列索引。因为当 sql 语句所查询的列,全部都出现在复合索引中时,此时由于 Oracle 只需要查询索引块即可获得所有数据,当然比使用多个单列索引要快得多;
–索引利于 select,但对经常 insert,delte 尤其 update 的表,会降低效率。
eg:试比较下面两条 SQL 语句(emp 表的 deptno 列上建有 ununique index):
语句 A:SELECT dname, deptno FROM dept WHERE deptno NOT IN (SELECT deptno FROM emp);
语句 B:SELECT dname, deptno FROM dept WHERE NOT EXISTS(SELECT deptno FROM emp WHERE dept.deptno = emp.deptno);
这两条查询语句实现的结果是相同的,但是执行语句 A 的时候,ORACLE 会对整个 emp 表进行扫描,没有使用建立在 emp 表上的 deptno 索引,执行语句 B 的时候,由于在子查询中使用了联合查询,ORACLE 只是对 emp 表进行的部分数据扫描,并利用了 deptno 列的索引,所以语句 B 的效率要比语句 A 的效率高。
—-where 子句中的这个字段,必须是复合索引的第一个字段;
eg:一个索引是按 f1, f2, f3 的次序建立的,若 where 子句是 f2 = : var2, 则因为 f2 不是索引的第 1 个字段,无法使用该索引。
—- where 子句中的这个字段,不应该参与任何形式的计算:任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。 —-应尽量熟悉各种操作符对 Oracle 是否使用索引的影响:以下这些操作会显式
(explicitly)地阻止 Oracle 使用索引: is null ; is not null ; not in; !=; like ; numeric_col+0;date_col+0; char_col||’ ‘; to_char; to_number,to_date 等。
Eg:
Select jobid from mytabs where isReq=’0’ and to_date (updatedate) >= to_Date ( ‘2001-7-18’,
‘YYYY-MM-DD’);–updatedate 列的索引也不会生效。
在正确使用索引的前提下,索引可以提高检索相应的表的速度。当用户考虑在表中使用索引时,应遵循下列一些基本原则。
(1)在表中插入数据后创建索引。在表中插入数据后,创建索引效率将更高。如果在装载数据之前创建索引,那么插入每行时oracle都必须更改索引。
(2)索引正确的表和列。如果经常检索包含大量数据的表中小于15%的行,就需要创建索引。为了改善多个表的相互关系,常常使用索引列进行关系连接。
(3)主键和唯一关键字所在的列自动具有索引,但应该在与之关联的表中的外部关键字所在的列上创建索引。
(4)合理安排索引列。在create index 语句中,列的排序会影响查询的性能,通常将最常用的列放在前面。创建一个索引来提高多列的查询效率时,应该清楚地了解这个多列的索引对什么列的存取有效,对什么列的存取无效。
(5)限制表中索引的数量。尽管表可以有任意数量的索引,可是索引越多,在修改表中的数据时对索引做出相应更改的工作量也越大,效率也就越低。同样,目前不用的索引应该及时删除。
(6)指定索引数据块空间的使用。创建索引时,索引的数据块是用表中现存的值填充的,直到达到PCTFREE为止。如果打算将许多行插入到被索引的表中,PCTFREE就应设置得大一点,不能给索引指定PCTUSED。
(7)根据索引大小设置存储参数。创建索引之前应先估计索引的大小,以便更好地促进规划和管理磁盘空间。单个索引项的最大值大约是数据块大小的一半。
16.6、删除索引
drop index PK_DEPT1;
16.7、索引类型
B树索引 B-tree indexes;
B树索引又可分为以下子类:
索引组织表Index-organized tables;
反转索引Reverse key indexes;
降序索引Descending indexes;
B树聚簇索引B-tree cluster indexes;
位图和位图联合索引Bitmap and bitmap join indexes;
基于函数的索引Function-based indexes;
应用域索引Application domain indexes;
B-Tree是一个平衡树的结构【注意这里的B表示Balanced平衡的意思,而不是Binary二叉】,B树索引也是Oracle里最为常见的索引类型。B树索引里的数据是已经按照关键字或者是被索引字段事先排好序存放的,默认是升序存放。
对于这幅B树存储结构图作以下几点介绍:
1 、索引高度是指从根块到达叶子块时所遍历的数据块的个数,而索引层次=索引高度-1;本图中的索引的高度是3,索引层次等于2;通常,索引的高度是2或者3,即使表中有上百万条记录,也就意味着,从索引中定位一个键字只需要2或3次I/O,索引越高,性能越差;
2、 B树索引包含两种数据块儿:分枝块(Branch Block)和叶子块(Leaf Block);
3 、分枝块里存放指向下级分枝块(索引高度大于2,即有超过两层分枝块的情况)或者直接指向叶子块的指针(索引高度等于2,即层次为1的索引);
4 、叶子块,就是位于B树结构里最底层的数据块。叶子块里存放的是索引条目,即索引关键字和rowid,rowid用来精确定位表里的记录;索引条目都是按照索引关键字+rowid已经排好序存放的;同一个叶子块里的索引条目同时又和左右兄弟条目形成链表,并且是一个双向链表;
5 、B树索引的所有叶子块一定位于同一层上,这是由B树的数据结构定义的。因此,从根块到达任何一个叶子块的遍历代价都是相同的;
B 树索引(B-Tree Index)
创建索引的默认类型,结构是一颗树,采用的是平衡 B 树算法:
l 右子树节点的键值大于等于父节点的键值 l 左子树节点的键值小于等于父节点的键值
位图索引(BitMap Index)
如果表中的某些字段取值范围比较小,比如职员性别、分数列 ABC 级等。只有两个值。
这样的字段如果建 B 树索引没有意义,不能提高检索速度。这时我们推荐用位图索引
Create BitMap Index student on(sex);
索引按功能和索引对象分还有以下类型。
(1)唯一索引意味着不会有两行记录相同的索引键值。唯一索引表中的记录没有RowID,不能再对其建立其他索引。在oracle10g中,要建立唯一索引,必须在表中设置主关键字,建立了唯一索引的表只按照该唯一索引结构排序。
(2)非唯一索引不对索引列的值进行唯一性限制。
(3)分区索引是指索引可以分散地存在于多个不同的表空间中,其优点是可以提高数据查询的效率。
(4)未排序索引也称为正向索引。Oracle10g数据库中的行是按升序排序的,创建索引时不必指定对其排序而使用默认的顺序。
(5)逆序索引也称反向索引。该索引同样保持列按顺序排列,但是颠倒已索引的每列的字节。
按照索引所包含的列数可以把索引分为单列索引和复合索引。索引列只有一列的索引为单列索引,对多列同时索引称为复合索引。
16.8、管理索引
1)先插入数据后创建索引
向表中插入大量数据之前最好不要先创建索引,因为如果先建立索引。那么在插入每行数据的时候都要更改索引。这样会大大降低插入数据的速度。
2)设置合理的索引列顺序
3)限制每个表索引的数量
4)删除不必要的索引
5)为每个索引指定表空间
6)经常做 insert,delete 尤其是 update 的表最好定期 exp/imp 表数据,整理数据,降低碎片(缺点:要停应用,以保持数据一致性,不实用);
有索引的最好定期 rebuild 索引(rebuild期间只允许表的 select 操作,可在数据库较空闲时间提交),以降低索引碎片,提高效率
16.8、索引问题
1.一个表的查询语句可以同时用到两个索引。
2.索引是以独立于表存在的一种数据库对象,它是对基表的一种排序(默认是 B 树索引就是二叉树的排序方式),比如:
3.这样的查询效率,肯定是大于没有索引情况的全表扫描(table access full),但是有两个问题。
问题一:建立索引将占用额外的数据库空间,更重要的是增删改操作的时候,索引的排序也必须改变,加大的维护的成本 问题二:如果经常查询 x=?和 y=?,那推荐使用组合 index(x,y),这种情况下组合索引的效率是远高于两个单独的索引的。
同时在用组合索引的时候,大家一定要注意一个细节:建立组合索引 index(x,y,z)的时候,那在查询条件中出现 x,xy,xyz,yzx 都是可以用到该组合索引,但是 y,yz,z 是不能用到该索引的。
第十三章 序列、同义词
13.1、 创建序列(sequence)
CREATE SEQUENCE seq
INCREMENT BY 2–增量
START WITH 2–起始值 不能小于min
MAXVALUE 10–最大值
MINVALUE 1–最小值
CYCLE–/NOCYCLE 序列号是否可循环(到了maxvalue在从min开始)
CACHE 5–/NOCACHE 缓存下一个值,必须满足大于1,小于等于(MAXVALUE-MINVALUE)/INCREMENT
NOORDER–/NOORDER 序列号是否顺序产生
13.2、 属性 NextVal,CurrVal
–当前值 —–currval
SELECT seq.currval FROM dual;
–下一个值——nextval
SELECT seq.nextval FROM dual;
(必须先有 nextval,才能有 currval)
–在向表中插入数据时使用
INSERT INTO emp1(empno) VALUES(seq.nextval);
使用 cache 或许会跳号, 比如数据库突然不正常 down 掉
(shutdown abort),cache中的sequence就会丢失. 所以可以在create sequence的时候用nocache防止这种情况
13.3、 修改
不能改变当前值,但是可以改变增量
ALTER SEQUENCE seq INCREMENT BY 3;
ALTER SEQUENCE seq CACHE 3;
13.4、 删除
DROP SEQUENCE seq;
13.5、 同义词 (synonym)
Select * from dual;
为什么?因为同义词的存在
Dual 其实是 sys 用户下的一张表
select table_name from user_tables where lower(table_name) = ‘dual’;
1、概念
同义词是数据库方案对象的一个别名,经常用于简化对象访问和提高对象访问的安全性。在使用同义词时,Oracle数据库将它翻译成对应方案对象的名字。与视图类似,同义词并不占用实际存储空间,只有在数据字典中保存了同义词的定义。在Oracle数据库中的大部分数据库对象,如表、视图、同义词、序列、存储过程、包等等,数据库管理员都可以根据实际情况为他们定义同义词。
同义词,顾名思义就是两个词的意思一样,可以互相替换.
2、作用:
1) 多用户协同开发中,可以屏蔽对象的名字及其持有者。如果没有同义词,当操作其他用户的表时,必须通过user名.object名的形式,采用了Oracle同义词之后就可以隐蔽掉user名,当然这里要注意的是:public同义词只是为数据库对象定义了一个公共的别名,其他用户能否通过这个别名访问这个数据库对象,还要看是否已经为这个用户授权。
2) 简化sql语句。上面的一条其实就是一种简化sql的体现,同时如果自己建的表的名字很长,可以为这个表创建一个Oracle同义词来简化sql开发。
3)为分布式数据库的远程对象提供位置透明性。
4)Oracle同义词在数据库链接中的作用
数据库链接是一个命名的对象,说明一个数据库到另一个数据库的路径,通过其可以实现不同数据库之间的通信。
Create database link 数据库链名 connect to user名 identified by 口令 using ‘Oracle连接串’;
访问对象要通过 object名@数据库链名。同义词在数据库链中的作用就是提供位置透明性。
3、分类:
Create synonym dept for soctt.dept;(这样创建的同义词是私有的,只有创建者才能用)
Drop synonym dept;
Create public synonym dept for soctt.dept;(这样创建的同义词才是公有的)
Drop public synonym dept;
4、权限管理
与同义词相关的权限有CREATE SYNONYM、CREATE ANY SYNONYM、CREATE PUBLIC SYNONYM权限。
1:用户在自己的模式下创建私有同义词,这个用户必须拥有CREATE SYNONYM权限,否则不能创建私有同义词。
2:如果需要在其它模式下创建同义词,则必须具有CREATE ANY SYNONYM的权限。
3:创建公有同义词则需要CREATE PUBLIC SYNONYM系统权限。
5、查看同义词
SQL> SELECT * FROM DBA_SYNONYMS WHERE SYNONYM_NAME IN ( ‘SYSN_TEST’,’PUBLIC_TEST’);
OWNER SYNONYM_NAME TABLE_OWNER TABLE_NAME DB_LINK
PUBLIC PUBLIC_TEST ETL TEST
ETL SYSN_TEST ETL TEST
SQL> SELECT * FROM USER_SYNONYMS
6、使用同义词
SELECT * FROM SYSN_TEST;
使用同义词可以保证当数据库的位置或对象名称发生改变时,应用程序的代码保持稳定不变,仅需要改变同义词;
当使用一个没有指定schema的同义词是,首先在用户自己的schema中寻找,然后再公共同义词中寻找
7、删除同义词
DROP [ PUBLIC ] SYNONYM [ schema. ] 同义词名称 [ FORCE ];
DROP SYNONYM SYSN_TEST;
DROP PUBLIC SYNONYM PUBLIC_TEST;–当同义词的原对象被删除是,同义词并不会被删除
8、编译同义词
ALTER SYNONYM T COMPILE; –当同义词的原对象被重新建立时,同义词需要重新编译
对原对象进行DDL操作后,同义词的状态会变成INVALID;当再次引用这个同义词时,同义词会自动编译,状态会变成VALID,无需人工干预,当然前提是不改变原对象的名称
问题锦集
1:公用同义词与私有同义词能否同名呢?如果可以,访问同义词时,是共有同义词还是私有同义词优先?
可以,如果存在公用同义词和私有同义词同名的情况,在访问同义词是,访问的是私有同义词的指向的对象。
2:为啥OE用户创建的公用同义词,HR用户不能访问呢?
因为HR没有访问OE模式下对象的权限,如果OE模式给HR用户赋予了SELECT对象等权限,那么HR用户即可访问。
3:对象、私有同义词、公共同义词是否可以存在三者同名的情况?
存在同名对象和公共同义词时,数据库优先选择对象作为目标,存在同名私有对象和公共对象时,数据库优先选择私有同义词作为目标
in 只读
out 只写
in out 可读写
函数就是一个有返回值的过程。
定义一个函数:此函数可以根据雇员的编号查询出雇员的年薪
CREATE OR REPLACE FUNCTION myfun(eno emp.empno%TYPE)
RETURN NUMBER
AS
rsal NUMBER ;
BEGIN
SELECT (sal+nvl(comm,0))*12 INTO rsal FROM emp WHERE empno=eno ;
RETURN rsal ;
END ;
/
直接写 SQL 语句,调用此函数:
SELECT myfun(7369) FROM dual ;
15.1分类:
DML触发器———–基于表的(insert、alter、update)
替代触发器———–基于VIEW的
系统触发器———–基于系统的
好处:自动调用、记录日志、保证数据安全、用数据库触发器可以保证数据的一致性和完整性。
语法:
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE | AFTER }
{INSERT | DELETE | UPDATE [OF column [, column …]]}
[OR {INSERT | DELETE | UPDATE [OF column [, column …]]}…]
ON [schema.]table_name | [schema.]view_name
[REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}]
[FOR EACH ROW ]
[WHEN condition]
PL/SQL_BLOCK | CALL procedure_name;
BEFORE 和AFTER指出触发器的触发时序分别为前触发和后触发方式,前触发是在执行触发事件之前触发当前所创建的触发器,后触发是在执行触发事件之后触发当前所创建的触发器。
FOR EACH ROW选项说明触发器为行触发器。行触发器和语句触发器的区别表现在:行触发器要求当一个DML语句操走影响数据库中的多行数据时,对于其中的每个数据行,只要它们符合触发约束条件,均激活一次触发器;而语句触发器将整个语句操作作为触发事件,当它符合约束条件时,激活一次触发器。当省略FOR EACH ROW 选项时,BEFORE 和AFTER 触发器为语句触发器,而INSTEAD OF 触发器则只能为行触发器。
REFERENCING 子句说明相关名称,在行触发器的PL/SQL块和WHEN 子句中可以使用相关名称参照当前的新、旧列值,默认的相关名称分别为OLD和NEW。触发器的PL/SQL块中应用相关名称时,必须在它们之前加冒号(:),但在WHEN子句中则不能加冒号。
WHEN 子句说明触发约束条件。Condition 为一个逻辑表达时,其中必须包含相关名称,而不能包含查询语句,也不能调用PL/SQL 函数。WHEN 子句指定的触发约束条件只能用在BEFORE 和AFTER行触发器中,不能用在INSTEAD OF 行触发器和其它类型的触发器中。
15.2、 触发器触发次序
1. 执行 BEFORE语句级触发器;
2. 对与受语句影响的每一行:
l 执行 BEFORE行级触发器
l 执行 DML语句
l 执行 AFTER行级触发器
3. 执行 AFTER语句级触发器
15.3、语句触发器
after 语句触发器
Before 语句触发器
例如:禁止工作人员在休息日改变雇员信息
create or replace trigger tr_src_emp
before insert or update or delete
on emp
begin
if to_char(sysdate,’DY’,’nls_date_language=AMERICAN’) in( ‘SAT’,’SUN’) then
raise_application_error(-20001,’can?t modify user information in weekend’);
end if;
end;
/
使用条件谓语———inserting、updating、deleting
create or replace trigger tr_src_emp
before insert or update or delete
on emp
begin
if to_char(sysdate,’DY’) in( ‘星期六’,’星期天’) then
case
when inserting then
raise_application_error(-20001,’fail to insert’);
when updating then
raise_application_error(-20001,’fail to update’);
when deleting then
raise_application_error(-20001,’fail to delete’);
end case;
end if;
end;
/
15.4、行触发器
执行 DML 操作时,每作用一行就触发一次触发器。
Bofre 行触发器
例如:确保员工工资不能低于原有工资
Create or replace trigger tr_emp_sal
before update of sal
on emp
for each row
begin
if :new.sal<:old.sal then
raise_application_error(-20010,’sal should not be less’);
end if;
end;
/
after 行触发器
例如:统计员工工资变化
Create table audit_emp_change(
Name varchar2(10),
Oldsal number(6,2),
Newsal number(6,2),
Time date);
Create or replace trigger tr_sal_sal after update of sal on emp for each row declare v_temp int;
begin
select count(*) into v_temp from audit_emp_change where name=:old.ename; if v_temp=0 then
insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate); else
update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
end if; end;
/
限制行触发器
Create or replace trigger tr_sal_sal
after update of sal
on emp
for each row when (old.job=?SALESMAN?)
declare
v_temp int;
begin
select count(*) into v_temp from audit_emp_change where name=:old.ename;
if v_temp=0 then
insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
else
update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
end if;
end;
/
注意:
例如:如果要基于 EMP 表建立触发器。那么该触发器的执行代码不能包含对 EMP 表的查询操作编写 DML 触发器的时,触发器代码不能从触发器所对应的基表中读取数据。
Create or replace trigger tr_emp_sal
Before update of sal
on emp
For each row
declare
Maxsal number(6,2);
Begin
If :new.sal>maxsal then Select max(sal) into maxsal from emp;
Raise_application_error(-21000,?error?);
End if;
End;
/
创建的时候不会报错。但是一旦执行就报错了
update emp set sal=sal*1.1 where deptno=30
DML触发器的限制
l CREATE TRIGGER语句文本的字符长度不能超过32KB;
l 触发器体内的SELECT 语句只能为SELECT … INTO …结构,或者为定义游标所使用的SELECT语句。
l 触发器中不能使用数据库事务控制语句 COMMIT; ROLLBACK, SVAEPOINT 语句;
l 由触发器所调用的过程或函数也不能使用数据库事务控制语句;
l 触发器中不能使用LONG, LONG RAW 类型;
l 触发器内可以参照LOB 类型列的列值,但不能通过 :NEW 修改LOB列中的数据;
:NEW 修饰符访问操作完成后列的值
:OLD 修饰符访问操作完成前列的值
特性
INSERT
UPDATE
DELETE
OLD
NULL
实际值
实际值
NEW
实际值
实际值
NULL
15.4 替代触发器
语法:
CREATE [OR REPLACE] TRIGGER trigger_name
INSTEAD OF
{INSERT | DELETE | UPDATE [OF column [, column …]]}
[OR {INSERT | DELETE | UPDATE [OF column [, column …]]}…]
ON [schema.] view_name –只能定义在视图上
[REFERENCING {OLD [AS] old | NEW [AS] new| PARENT as parent}]
[FOR EACH ROW ] –因为INSTEAD OF触发器只能在行级上触发,所以没有必要指定
[WHEN condition]
PL/SQL_block | CALL procedure_name;
例2:创建复杂视图,针对INSERT操作创建INSTEAD OF触发器,向复杂视图插入数据。
l 创建视图:
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;
l 创建触发器:
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;
创建INSTEAD OF触发器需要注意以下几点:
l 只能被创建在视图上,并且该视图没有指定WITH CHECK OPTION选项。
l 不能指定BEFORE 或 AFTER选项。
l FOR EACH ROW子可是可选的,即INSTEAD OF触发器只能在行级上触发、或只能是行级触发器,没有必要指定。
l 没有必要在针对一个表的视图上创建INSTEAD OF触发器,只要创建DML触发器就可以了。
15.5、系统事件触发器
语法:
CREATE OR REPLACE TRIGGER [sachema.]trigger_name
{BEFORE
{BEFORE|AFTER}
{ddl_event_list
{ddl_event_list | database_event_list}
ON { DATABASE | [schema.]SCHEMA }
[WHEN condition]
PL/SQL_block | CALL procedure_name;
其中: ddl_event_list:一个或多个DDL 事件,事件间用 OR 分开;
database_event_list:一个或多个数据库事件,事件间用 OR 分开;
下面给出系统触发器的种类和事件出现的时机(前或后):
事件
允许的时机
说明
STARTUP
AFTER
启动数据库实例之后触发
SHUTDOWN
BEFORE
关闭数据库实例之前触发(非正常关闭不触发)
SERVERERROR
AFTER
数据库服务器发生错误之后触发
LOGON
AFTER
成功登录连接到数据库后触发
LOGOFF
BEFORE
开始断开数据库连接之前触发
CREATE
BEFORE,AFTER
在执行CREATE语句创建数据库对象之前、之后触发
DROP
BEFORE,AFTER
在执行DROP语句删除数据库对象之前、之后触发
ALTER
BEFORE,AFTER
在执行ALTER语句更新数据库对象之前、之后触发
DDL
BEFORE,AFTER
在执行大多数DDL语句之前、之后触发
GRANT
BEFORE,AFTER
执行GRANT语句授予权限之前、之后触发
REVOKE
BEFORE,AFTER
执行REVOKE语句收权限之前、之后触犯发
RENAME
BEFORE,AFTER
执行RENAME语句更改数据库对象名称之前、之后触犯发
AUDIT / NOAUDIT
BEFORE,AFTER
执行AUDIT或NOAUDIT进行审计或停止审计之前、之后触发
/*create or replace trigger tr_emp1_1
–before update on emp1
after update on emp1
for each row
declare
– local variables here
begin
if :new.sal<:old.sal then
Raise_application_error(-20000, ‘:new.sal>:old.sal’);
end if;
end tr;*/
/*create or replace trigger tr_emp1_2
after delete on emp1
–before delete on emp1
for each row
begin
Raise_application_error(-20001, ‘不能删’);
end;*/
create or replace trigger tr_emp1_3
after delete or update or insert on emp1
for each ROW
begin
if deleting then
if :new.sal>0 then
Raise_application_error(-20000, ‘deleting’);
end IF;
/*elsif updating then
if :new.sal<:old.sal then
Raise_application_error(-20001, ‘updating’);
end if;
else
if :new.sal<:old.sal then
Raise_application_error(-20002, ‘inserting’);
end if;*/
end if;
end;
16.1、存储过程
CREATE OR REPLACE PROCEDURE procedure_name AS PL/SQL块
现在定义一个简单的过程,就是打印一个数字
CREATE OR REPLACE PROCEDURE myproc AS
i NUMBER ;
BEGIN
i := 100 ;
DBMS_OUTPUT.put_line(‘i = ‘||i) ;
END ;
/
执行过程: exec 过程名字———set serveroutput on
下面编写一个过程,要求,可以传入部门的编号,部门的名称,部门的位置,之后调用此过程就可以完成部门的增加操作。
CREATE OR REPLACE PROCEDURE myproc(dno dept.deptno%TYPE,
name dept.dname%TYPE,
dl dept.loc%TYPE)
AS
cou NUMBER ;
BEGIN
– 判断插入的部门编号是否存在,如果存在则不能插入
SELECT COUNT(deptno) INTO cou FROM dept WHERE deptno=dno ;
IF cou=0 THEN
– 可以增加新的部门
INSERT INTO dept(deptno,dname,loc) VALUES(dno,name,dl) ; DBMS_OUTPUT.put_line(‘部门插入成功!’) ;
ELSE
DBMS_OUTPUT.put_line(‘部门已存在,无法插入!’) ;
END IF ;
END ;
/
16.2 过程的参数类型:
? IN:值传递,默认的—–只读
? IN OUT:带值进,带值出—可读写
? OUT:不带值进,带值出—-只写
IN OUT 类型:
CREATE OR REPLACE PROCEDURE myproc(dno IN OUT dept.deptno%TYPE,
name dept.dname%TYPE,
dl dept.loc%TYPE) AS
cou NUMBER ;
BEGIN
– 判断插入的部门编号是否存在,如果存在则不能插入
SELECT COUNT(deptno) INTO cou FROM dept WHERE deptno=dno ;
IF cou=0 THEN
– 可以增加新的部门
INSERT INTO dept(deptno,dname,loc) VALUES(dno,name,dl) ;
DBMS_OUTPUT.put_line(‘部门插入成功!’) ;
– 修改 dno 的值
dno := 1 ;
ELSE
DBMS_OUTPUT.put_line(‘部门已存在,无法插入!’) ; dno := -1 ;
END IF ;
END ;
/
编写 PL/SQL 块验证过程:
DECLARE
deptno dept.deptno%TYPE ; BEGIN
deptno := 12 ;
myproc(deptno,’开发’,’南京’) ;
DBMS_OUTPUT.put_line(deptno) ;
END ;
/
OUT 类型 不带任何值进,只把值带出来。
CREATE OR REPLACE PROCEDURE myproc(dno OUT dept.deptno%TYPE)
AS
I number
BEGIN
I:= dno;
END ;
/
执行上面的存储过程
DECLARE
deptno dept.deptno%TYPE ;
BEGIN
deptno :=myproc(deptno) ;
DBMS_OUTPUT.put_line(deptno) ;
END ;
/
17.1 定义
包——规范和主体
包就是将一系列的相关联的PLSQL类型、项目和子程序等有计划的组织起来封装在一起
规范(包头)———一个操作或应用的接口部分
主体———-对包的规范部分进行实现
语法;
包的规范
CREATE OR REPLACE PACKAGE package_name AS|IS
参数、类型(type)、异常(exception)、游标(cursor)、procedure、function
END[package_name];
包的主体
CREATE OR REPLACE PACKAGE BODY package_name AS|IS
参数、类型(type)、异常(exception)、游标(cursor)、procedure、function
END[package_name];
调用:
package_name.type_name;
package_name.item_name;
package_name.call_spec_name;
删除:
drop package package_name;
drop package body package_name;
好处:
1、模块化;—–提高应用程序的交互性
2、信息隐藏
例:
CREATE OR REPLACE PACKAGE DEMO_PKG
IS
DEPTREC DEPT%ROWTYPE;
–Add dept…
FUNCTION add_dept(
dept_no NUMBER,
dept_name VARCHAR2,
location VARCHAR2)
RETURN NUMBER;
–delete dept…
FUNCTION delete_dept(dept_no NUMBER)
RETURN NUMBER;
–query dept…
PROCEDURE query_dept(dept_no IN NUMBER);
END DEMO_PKG;
CREATE OR REPLACE PACKAGE BODY DEMO_PKG
IS
FUNCTION add_dept
(
dept_no NUMBER,
dept_name VARCHAR2,
location VARCHAR2
)
RETURN NUMBER
IS
empno_remaining EXCEPTION; –自定义异常
PRAGMA EXCEPTION_INIT(empno_remaining, -1);
/* -1 是违反唯一约束条件的错误代码 */
BEGIN
INSERT INTO dept VALUES(dept_no, dept_name, location);
IF SQL%FOUND THEN
RETURN 1;
END IF;
EXCEPTION
WHEN empno_remaining THEN
RETURN 0;
WHEN OTHERS THEN
RETURN -1;
END add_dept;
FUNCTION delete_dept(dept_no NUMBER)
RETURN NUMBER
IS
BEGIN
DELETE FROM dept WHERE deptno = dept_no;
IF SQL%FOUND THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END delete_dept;
PROCEDURE query_dept
(dept_no IN NUMBER)
IS
BEGIN
SELECT * INTO DeptRec FROM dept WHERE deptno=dept_no;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(‘温馨提示:数据库中没有编码为’||dept_no||’的部门’);
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE(‘程序运行错误,请使用游标进行操作!’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE||’—-‘||SQLERRM);
END query_dept;
BEGIN
Null;
END DEMO_PKG;