pl/sql学习(5): 触发器trigger/事务和锁

(一)触发器简单介绍

触发器是由数据库的特定时间来触发的, 特定事件主要包括以下几种类型:

(1)DML: insert, update,delete 增删改

(2)DDL: create, alter, drop;

(3) 数据库事件: logon,logoff,startup,shutdown,errors

 触发器可以分为五种类型

(1)DML触发器: insert , update, delete

(2)DDL 触发器: create,alter,drop

(3)复合触发器 为Oracle11g的新特性,一个触发器包含了4中类型

(4)instead of 触发器: 通常作用在视图上,一般对具有多个表源的视图进行DML操作是不允许的,遇到这种情况就可以利用instead of 类型触发器解决问题,

由此可以对视图的DML操作转化为对多个源表的操作.

(5)用户与系统事件触发器: 登陆 注销等

简单实例: 每次执行删除操作之后,都会信息提示:“这是删除操作!”

CREATE TRIGGER first_trigger
AFTER DELETE
ON emp  -- 作用在emp 表上
BEGIN
DBMS_OUTPUT.put_line('这是删除操作!');
END;

查看触发器:

-- 查找触发器的名称
select object_name from user_objects where onject_type='TRIGGER';
-- 得到名称后, 查找触发器的内容
select *  from user_source where name='first_trg' order by line;

(二)触发器的语法

1.DDL事件的触发器: create alter drop

CREATE [OR REPLACE] TRIGGER --触发器的名称
[BEFORE  | AFTER | INTEAD OF ] [DDL事件] ON [DATABASE | SCHEMA] 
[WHEN 触发条件] 
[DECLARE] 
    [程序的声明部分;] 
BEGIN
    程序的代码部分
END;

DDL 事件描述与触发动机

2.DML触发器

DML触发器可用于实现数据安全保护、数据审计、数据完整性、参照完整性、数据复制等功能。

包括语句触发器和行触发器。
1、语句触发器:在指定操作语句之前或者之后执行一次,不管这条语句影响了多少行。(针对行)
2、行触发器:触发语句作用的每一条记录都被触发,在行级触发器中使用:old和:new伪记录变量,识别值的状态。(针对表):old表示操作该行之前,这一行的值;:new 表示操作该行之后,这一行的值。

语法:

CREATE [OR REPLACE] TRIGGER trigger_name 
{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [ OF 列名]} -- 触发条件具体到某一列
ON 表名
[FOR EACH ROW [WHEN (条件)]) -- 表示行级触发器, 省略则为语句触发器
PLSQL块

实例1: 禁止在休息日(周六、周日)改变emp表的数据。

--思路: 使用to_char(sysdate, 'day')函数; 采用语句触发器。
CREATE OR REPLACE TRIGGER emp_trigger
BEFORE INSERT OR UPDATE OR DELETE
ON emp
BEGIN
IF to_char(sysdate, 'day') IN ('星期六', '星期日') THEN
RAISE_APPLICATION_ERROR(-20006, '不能在休息日改变员工信息!');
END IF;
END;

注意:RAISE_APPLICAITON_ERROR是用来测试异常处理的存储过程,能够将应用程序专有的错误从服务器端转达到客户端应用程序中(其他机器上的SQLPlus或者其他前台开发语言),其存储过程有两个参数

RAISE_APPLICAITON_ERROR(error_number_in IN NUMBER, error_msg_in IN VARCHAR2); 
-- error_msg_in的长度不能超过2k,超过2k后会进行截取

实例2: 实现数据审计: 审计员工信息表数据的变化,审计删除时间,以及被删除的雇员名。

已知表emp_temp (name varchar2(10), delete_time  date) 用于存放被删除的员工姓名与删除日期.

CREATE OR REPLACE TRIGGER del_emp_trigger
AFTER DELETE ON emp
FOR EACH ROW
BEGIN
    INSERT INTO emp_temp VALUES(:old.ename, SYSDATE); --插入被删除员工的姓名和当前时间
END;

注意: SQL语句和PLSQL语句中,old和new伪记录变量需要加上冒号: , 而在when这样的限制性条件语句当中,则不需要加上冒号:。 

实例3: 员工涨后工资不能低于原来的工资,且所涨的工资不能超过原工资的50%。

CREATE OR REPLACE TRIGGER tr_check_sal
BEFORE UPDATE OF sal ON emp
FOR EACH ROW 
WHEN (new.sal < old.sal OR new.sal > old.sal * 1.5)
BEGIN
RAISE_APPLICATION_ERROR(-20028, '工资只升不降,并且升幅不能超过50%');
END;

3. instead of 触发器: 适用于视图上的触发器 

为什么要用instead of 触发器? 

在简单视图上往往可以执行INSET、UPDATE、DELETE操作。

但是在复杂视图上执行INSET、UPDATE、DELETE操作时有限制。所以为了在这些复杂的视图上执行DML操作,需要建立替代触发器。 

限制:

(1)替代触发器只适用于视图。

(2)替代触发器不能指定BEFORE和AFTER选项。

(3)不能在具有WITH CHECK OPTION选项的视图上建立替代触发器。

(4)替代触发器必须包含FOR EACH ROW选项。

实例: 

CREATE OR REPLACE VIEW emp_dept  --创建一个视图
AS 
SELECT d.deptno, d.dname, e.empno, e.ename
FROM dept d, emp e
WHERE d.deptno = e.deptno;

当没有创建替代触发器时,对emp_dept视图插入数据出错:

INSERT INTO emp_dept VALUES(50, 'DEVELOPMENT', 2222, 'ALICE');
INSERT INTO emp_dept VALUES(50, 'DEVELOPMENT', 2222, 'ALICE');
-- 报错
--ORA-01779: 无法修改与非键值保存表对应的列

创建emp_dept视图(复杂视图)的替代触发器:

CREATE OR REPLACE TRIGGER instead_of_trigger
INSTEAD OF 
INSERT
ON emp_dept
FOR EACH ROW
DECLARE
    v_temp INT;
BEGIN
    SELECT COUNT(*) INTO v_temp FROM dept WHERE deptno = :new.deptno;
    IF v_temp = 0 THEN -- 没有这个部门则插入这个条新数据
        INSERT INTO dept(deptno, dname) VALUES(:new.deptno, :new.dname);
    END IF;
    SELECT COUNT(*) INTO v_temp FROM emp WHERE empno = :new.empno;
    IF v_temp = 0 THEN -- 没有这个员工信息就插入这条新数据
        INSERT INTO emp(empno, ename, deptno) VALUES(:new.empno, :new.ename, :new.deptno);
    END IF;
END;

4. 系统触发器

实例: 创建记录发生的数据库系统EVENT_TALBE事件表:

CREATE OR REPLACE TRIGGER startup_trigger
AFTER STARTUP ON DATABASE
BEGIN
INSERT INTO event_table VALUES(ORA_SYSEVENT, SYSDATE);
END;

 (三)事务和锁

1.什么是事务?

事务可以看做是由对数据库的若干操作组成的一个单元,这些操作要么都完成,要么都取消,从而保证数据满足一致性的要求。

事务和锁保证了数据库的一致性. 因为数据库是一个由多个用户共享的资源, 因此当多个用户并发的存储数据时, 为了保证数据库的准确性, 需要用事务和锁.

2.事务的组成:一条或者多条DML、一条DDL或者一条DCL语句。(DML语句需要使用COMMIT提交事务或者使用ROLLBACK回滚事务,而DDL和DCL是自动提交事务的。)

注意: DML:data manipulation language 数据操作语句: select update insert delete 需要提交与回滚事务 增删改

DDL : data definition language: 数据库定义语句 create ,alter, drop

DCL: data control language 数据库控住语句  grant deny revoke  设置更改数据库用户的角色或者权限

3.为什么要使用事务?

是为了保证数据的安全有效。

每一个事务都是一个原子单元, 事务中的语句可以被作为一个整体, 要么一起被提交, 作用在数据库上使得数据库的数据永久被修改;

 

要么一起被撤销, 对数据库不做任何修改.  如当执行事务操作(DML语句)时,Oracle会在被作用表上加表锁,以防止其他用户改变表结构;

同时会在被作用行上加行锁,以防止其他事务在相应行上执行DML操作.

4.事务的控制命令

(1) set transaction 设置事务的属性;

(2)提交事务(COMMIT):通过COMMIT语句可以提交事务,当执行了COMMIT语句后,会确认事务的变化、结束事务、删除保存点、释放锁。当使用COMMIT语句结束事务之后,其他会话可以查看到事务变化后的新数据。

(3)回滚事务(ROLLBACK):只能对未提交的数据撤销,已经commit的数据无法撤销。

(4)保存点(SAVEPOINT):用于取消部分事务,当结束事务时,会自动的删除该事务所定义的所有保存点。当执行ROLLBACK时,通过指定保存点可以回退到指定的点。(设置保存点:SAVEPOINT a;, 回滚部分事务:ROLLBACK TO a;,回滚全部事务:ROLLBACK;)

(5)rollback to savepoint : 回滚到保存点.

例子: 银行账户之间的汇款转账操作 需要三个步骤

(1) 源账户减少存储金额, 例如-1000;

(2)目标账户增加存储金额1000;

(3) 在事务日志中记录该事务.

将整个交易看做是一个事务, 如果操作失败, 那么该事务就会 rollback 回滚, 目标账户与源账户的金额都不会变化.

注意: 事务在没有提交之前可以回滚, 而且在提交前当前用户还可以查看 已经修改的数据, 但是其他用户看不到, 一旦提交事务就没法修改了

 

5.事务的类型:

(1) 显示方式 

所谓显示方式就是利用命令完成, 语法为

新事务开始
SQL statement  -- 若干条SQL语句
...
commit| rollback; -- 提交或回滚

Oracle 中的事务不需要设置开始标志,通常 在 (1) 登陆数据库,第一次执行DML 语句;(2) 事务结束后, 第一次执行DML语句时 , 事务就会开启.

(2) 隐式方式

隐式方式的事务没有明确的开始与结束标志, 它从数据库自动开启, 到一个程序正常结束或使用DDL语句时会自动提交, 操作失败也会自动回滚.

如果设置autocommit 为打开状态(默认关闭), 则每次执行DML操作就会自动提交事务.

set autocommit on/off;

需要注意事务在什么时候结束, 主要有以下几种情况

(1) 使用commit, rollback

(2) 执行DDL, 事务自动提交,例如 create, alter, drop, revoke,grant 等

(3) 正常退出SQL*plus 时, 非正常退出则rollback

例子:

新建一个sql*plus1, 执行以下语句 emp(ename,eno,job)

inset into emp values('slice','125','manager');

此时select * from emp ,可以看到这条数据被插入进去了. 但是注意: 此时还没有提交事务!!

同样的, 登陆新的sql*plus,  窗口为sql*plus2, 此时查看发现emp并没有这条数据. 

在sql*plus1窗口提交事务 commit; 后, sql*plus2窗口查询emp 的数据才会显示出来.

6.事务的保存点

保存点可以设置在事务中的任何地方, 也可以设置多个点, 可以将较长的事务分成较小的段, 好处是当操作有问题时, 不会全部回滚, 回滚到保存点处.

一旦事务回滚到某个保存点后, Oracle 会把保存点之后持有的锁释放掉, 此时先前当等待被锁资源的事务就可以继续了.

如何使用保存点, 很简单, 只有一行代码

insert into emp values ('alice', '125','manager'); -- 向emp表中添加一条数据
savepoint fst; --创建保存点
--创建成功后, 继续增加一条数据
insert into emp values ('mike', '126','manager'); -- 

此时查看emp可以看到新增的两条数据, 执行 rollback to fst;

事务成功回滚到保存点处, 第二条数据看不到了.

7.事务的ACID特性

A: 原子性(不可分割, 要么全部执行,要么全部撤销)

C: 一致性

I: 隔离性(不同事务互不干扰)

D: 持久性(一旦提交为对数据库的永久修改)

(三) 锁

1.什么是锁

同一时刻多个用户同时操作相同资源的情况时有发生, 数据库利用锁可以消除多个用户同时造作同一资源产生的隐患.

锁是一种机制, 在访问相同资源时, 可以防止事务之间的破坏性交互.例如, 多个会话同时操作某表时, 优先操作的会话需要持有该表的锁.

锁 可以确保多个会话像队列一样依次进行.

注意:

(1)Oracle 提供了很大程度的并发性: 会话1在修改一条记录, 仅仅该记录会锁定, 其他会话可以随时进行读取操作, 但是读取的为修改前的数据.

(2) 会话1对表emp 的某行记录进行修改时, 另一个会话2 也来修改这个记录, 在没有任何处理情况下最后保存的数据有随机性, 这种数据叫做"脏数据"

如果采用了行级锁, 会话1 修改记录时锁定该行,会话2 只能等待, 避免脏数据的产生.

2.锁的分类

 (1) 排他锁: 防止资源的共享, 用于数据的修改, 若事务1给数据A加锁, 其他事务不能对A加任何锁, 此时只允许事务1 对A进行读取和修改, 直到事务完成释放锁为止.

(2) 共享锁(也成为 读锁) : 该模式下锁的数据只能被读取, 不能被修改, 若事务1给数据A加锁, 其他事务不能对A加任何锁, 只能共享锁,  加了该锁的数据可以并发的被读取.

锁 很多都是数据库自动管理, 当事务提交后自动释放锁.

3. 锁的类型

(1) DML锁: 也叫数据锁, 用于保护数据.

(2)DDL锁: 保护模式中对象的结构

(3)内部闩锁: 保护数据库的内部结构, 完全自动调用.

其中DML锁还可以再细分为:

(1) 行级锁TX: 也成为 事务锁, 当修改表中某行记录时, 需要给它加行级锁, 防止两个事务同时修改相同记录. 事务结束 锁也会自动释放, 是粒度最小的锁. --? belong 排它锁

(2)表级锁TM: 为了防止修改表的数据时表结构发生变化. 会话1在修改表emp时 会得到表emp 的TM锁, 而此时不允许其他会话会该表进行变更或删除操作.

例子: 打开 sql*plus ,窗口sql*plus 1, 修改表emp , DML操作

update emp set sal=sal+100 where eno='123';

此时再打开另一个窗口sql*plus2 , 对该表进行DDL操作.

 drop table emp; 
--会报错
-- ORA-00054 资源正忙, 但指定以nowait方式打开获取资源, 或者超时失败

在执行DML操作(增删改)时, 数据库会先申请数据库对象上的共享锁, 防止其他的会话对该对象执行DDL(创删改)操作.

4. 锁等待/死锁

有时候由于占用的资源不能及时释放, 造成锁等待,这严重影响数据库的性能与日常工作, 

例如在窗口sql*plus 1 中修改emp 的数据

update emp set sal=sal+100 where eno='123';

虽然sql*plus 1 窗口已经提示更新, 但是事务没有提交!

此时再打开另一个窗口sql*plus2 , 同样也执行修改, 执行上述同样的语句,  不是提示已更新, 而是一直等待. 

这是因为sql*plus 1 封锁了记录, 但是事务没有结束, 锁不会释放.

 死锁 是锁等待的一个特例, 通常发生在两个或者多个会话之间.

假设一个会话要修改两个资源对象, 可以是表也可以是字段, 修改这两个资源A,B的操作在一个事务S1中,

当S1修改了第一个对象A需要对其锁定, 这时另外一个会话 或者事务S2 也需要修改A, B, 并且已经获得并锁定了第二个对象B,

那么就出现了死锁, 这时两个会话/事务度得不到想要的结果.

例子:

--会话1, sql*plus1 中
update emp set sal=sal+100 where eno='125';
-- 会话2, sql*plus2 中
update emp set sal=sal+100 where eno='126';
--会话1, sql*plus1 中想修改第二个会话已经修改的记录, 则出现锁等待
update emp set sal=sal+100 where eno='126';
-- 会话2 想修改第1个会话已经修改的记录
update emp set sal=sal+100 where eno='125'; 
-- 检测到死锁!

介绍: 用企业OEM管理器终止锁阻塞!

 

posted @ 2018-08-30 17:15  xy小崽子  阅读(1071)  评论(0编辑  收藏  举报