1、事务

介绍事务的四大特性(原子性,一致性,隔离性,永久性)和相应的例子来演示

===============================================


银行账号里的转账, 就是一个好的学习事务例子:


假设有2个账号, A账号和B账号 。


A 账号 转给 B 账号 100块钱,

(2个动作在里面, 1是A账号减去100块,2是B账号增加100块钱 ,2个动作不可分割-原子性)


如果当 B账号钱没有增加的时候, 那么A账号的钱不应该减少, 保持一致性。


CREATE TABLE zhang_hao( id integer, zhang_hu varchar(25), jin_e integer);


INSERT INTO zhang_hao VALUES(1,'A',1000);


INSERT INTO zhang_hao VALUES(2,'B',500);

wKioL1Nfa5GRwzv0AAAPfUTjslg484.png

#commit与rollback

在commit之前,可以rollback回滚操作;

wKiom1NfbMLSqH4PAAA9libyjQI190.png

commit之后,就回滚不了操作了;

wKiom1NfbRPzRQL-AABCVHXLXZg959.png


#更新一条数据, 会出现2个用户同时更新的情况

在第一个用户修改完数据后并没有commit,就会造成第二个操作人员查到的还是修改之前的信息,

这就导致了操作误差。

wKiom1Nfb6DwPpDcAAC7TOG5_j4626.png

解决并发一个办法:


当我在更新的时候 其他用户不能进行修改,  可以说是加上一个排它锁(隔离性)。

SELECT * FROM zhang_hao FOR UPDATE;

这样sys账号 就不能更新,在一个等待的状态中,当scott用户执行了commit操作后,sys才可以更新数据;


#A给B汇钱100-A减少100

wKiom1NfdEXTlkv6AABeGHIRPJA755.png

#A给B汇钱100-B增加100

wKioL1NfdCjCgt3CAAAt_a51bGk963.png

#######

------------------------

下面一个完整的例子说明:


A账号转800块钱给B账号。


这就要注意一个问题, 当A账号的钱转出去了,中途出现错误,B账号没有收到。


这种情况我们就不应该减少A账号的钱, 不执行操作, 做一个回滚。



------------------------

下面代码演示上面所说的情况


先随便创建一张表


create table test( tt varchar(30) );


SQL> create or replace procedure zh_proc

 2  as

 3  begin

 4  savepoint mystart; 创建还原点

 5  update zhang_hao set jin_e=200 where zhang_hu='A';

 6  insert into test values('dd');

 7  update zhang_hao set jin_e=1300 where zhang_hu='B';

 8  commit;

 9  exception

10  when others then

11  rollback to mystart;

12  end;

13  /


也就是说:


 5  update zhang_hao set jin_e=200 where zhang_hu='A';

 6  insert into test values('dd');

 7  update zhang_hao set jin_e=1300 where zhang_hu='B';


这3条语句任意一条出现执行错误, 都会回滚 rollback 到 开始的地方 mystart 。把3条语句看成一个整体。


删除 test 表

SQL> drop table test;


目的为了 执行 SQL> drop table test; 出现错误


SQL> execute zh_proc;

BEGIN zh_proc; END;


     *

ERROR at line 1:

ORA-06550: line 1, column 7:

PLS-00905: object SCOTT.ZH_PROC is invalid

ORA-06550: line 1, column 7:

PL/SQL: Statement ignored


SQL> select * from zhang_hao;


       ID ZHANG_HU                       JIN_E

---------- ------------------------- ----------

        1 A                               1000

        2 B                                500


可以证明 update zhang_hao set jin_e=200 where zhang_hu='A';  这条语句被回滚了。

现在我们把test表新建回去


create table test( tt varchar(30) );


SQL> execute zh_proc;


PL/SQL procedure successfully completed.


SQL> select * from zhang_hao;


       ID ZHANG_HU                       JIN_E

---------- ------------------------- ----------

        1 A                                200

        2 B                               1300


这样他就会成功提交执行那3条sql语句。



2、触发器

#定义

触发器是一个特殊的存储过程。


区别就是在于, 存储过程需要去调用,而触发器无需调用,在执行某些操作的时候,会自动执行。


一般当表或者视图执行 增,删,改 操作的时候,就会自动执行触发器中的PL SQL 语句块。


还有一个区别, 创建触发器是不带参数的, 而 存储过程 可带可不带 参数。


===========================================

下面一个例子进行演示


数据库行级触发器 - 对每一行(每一条记录进行检查) 动作都触发 FOR EACH ROW


创建一个学生表:

CREATE TABLE xue_sheng( id integer, xing_ming varchar(25),xing_bie number, fen_shu number, b_id integer);


INSERT INTO xue_sheng VALUES(1,'ZhanSan',1,80,1);


INSERT INTO xue_sheng VALUES(2,'LiSi',1,90,2);


INSERT INTO xue_sheng VALUES(3,'ZhanHong',0,75,2);


INSERT INTO xue_sheng VALUES(4,'ChenXiaoMing',1,85,1);


创建一个班级表:

CREATE TABLE ban_ji( id integer , ban_ji varchar(25));


INSERT INTO ban_ji VALUES(1,'1-(1)');


INSERT INTO ban_ji VALUES(2,'1-(2)');



----------------------------------------------------------

wKioL1NfnAWRFjtDAAAn4t4R2oU371.png


#创建一个删除行级触发器 trigger

当删除班级表的一个id , 那么它会自动把学生表所属的班级的学生也会删除

wKioL1NfniuDxA2uAABG39FWvnA827.png

执行删除操作的时候, 建立一个 old内存表, old表和ban_ji表 结构完全一样

所以上面的 old.id 可以理解成 ban_ji班级表的 id


#创建一个插入行级触发器

wKiom1NfoLHRSITuAABMNo8jaXg593.png

当插入数据时候,先插入到 new 表,new表和班级表结构也是一样的。 然后在插入到 真正的表,

所以 new.id 和 ban_ji班级表id 对应的。


#更新trigger

级联更新 同时涉及到 old.id 和 new.id

wKioL1NfolfTAD5AAABNZIsuk3I045.png

总结一下触发器: 主要是对 old.id 和 new.id 的理解,还有要注意触发器建立所在表的选择。