关于Oracle和PLSQL的学习记录11

本文详细介绍了Oracle中的事务处理,包括事务的开始、提交、回滚、保存点的设置以及事务在遇到DDL和DCL语句时的行为。同时,讨论了避免脏读和不可重复读的情况,以及Oracle的锁机制。还提到了程序包的创建和使用,为数据库操作提供更高效和模块化的管理方式。

------事务处理和并发控制------
1.事务是Oracle中进行数据库操作的基本单位

2.事务开始
事务是由一个或多个SQL语句序列结合在一起所形成的一个逻辑处理单元
Oracle中的开始事务不需要通过类似begin等语句的显式表示,而是从执行第一条对表进行操作的DML语句开始

3.提交事务
在Oracle中,提交事务有两种方式,其表现形式分别如下:
commit 该语句是Oracle PLSQL显式提交事务的语句,执行commit语句后之前的DML语句组成的事务结束,数据写入对象中
DDL/DCL语句:如果在DML语句后执行了DDL(数据定义语句,如create\drop等)或DCL(数据控制语句,如grant\revoke等)语句,事务被提交

事务提交时事务结束的正常表现之一,还有多种原因可能造成事务结束,例如数据库连接断开、服务器宕机等

4.回滚事务
即当事务还没有提交时数据库服务器异常关闭了,那么事务中的所有操作都会被撤销
当用户要撤销事务中对数据的操作时,也可以选择手动回滚事务使数据表回到事务开始前的状态
update stu set sage = sage + 1;
delete from stu where sno = '100002';
rollback;
事务回滚仅仅是在逻辑上撤销事务操作,用户在数据库级所做的一些操作不会回到事务开始前的状态

5.设置保存点
上例中将事务开始回滚前的2个操作全部撤销了,这种操作称为回滚全部事务
在具体数据库操作中,事务中包含的操作往往很多,当用户需要回滚事务时,只需要撤销事务中的部分操作而非所有操作,此时就需要为事务设置保存点
update stu set sage = sage - 1;
savepoint updatepoin;
delete from stu where sno = '10001';
在Oracle中,savepoint时设置保存点命令,通常与rollback一起使用
即使设置了保存点,也并不代表保存点之前的操作不需要commit提交了,只有提交事务才能对数据表造成影响

6.回滚部分事务
创建保存点后,用户可以继续执行其他sql语句,如果用户需要撤销事务的部分操作,就可以通过回滚至保存点来实现
uodate stu set sage = sage + 1;
savapoint updatepoin;
delete from stu where sno = '10001';
rollback to updatepoin;
保存点之前对stu表做的操作不会被撤销
虽然没有撤销,但这些操作要写入数据表中还是必须通过commit提交事务

7.DDL语句对事务的影响
事实上,Oracle的事务从第一条SQL语句开始,碰到第一条DDL语句则结束事务
因此,可以认为DDL语句能够提交事务
insert into stu values('120009','里斯','男',20,'12艺术设计',to_date('21-08-1989','DD-MM-YYYY'));
alter table stu disable all triggers;
rollback;
上述当执行rollback试图撤销insert时是无法撤销的

在Oracle PLSQL中的DDL语句包含create、alter、drop、rename和truncate等5中语句

8.DCL语句提交事务
delete from stu where sno = '12008';
grant connect to test;
rollback;
同样,无法撤销delete语句操作

在Oracle PLSQL中的DCL语句包含grant授权语句和revoke收回授权两种语句

9.断开数据库连接对事务的影响
用户正常断开与数据库的连接时,Oracle会自动提交前面未被提交的事务,所有对数据表的操作都生效

10.异常断开数据库连接回滚事务
当用户异常断开数据库连接的时候,事务不会自动提交,而是被回滚

总的来说,Oracle数据库的事务是由一个或多个DML语句组成,事务中可以包含一个DDL/DCL语句;Oracle可以自动地标识一个事务,事务是以第一个可执行地SQL语句开始,当下列事件之一发生时结束:
用户执行了commit语句,事务被显式提交
用户执行了rollback语句,事务被回滚
用户执行了DDL语句,事务被自动提交
用户执行了DCL语句,事务被自动提交
用户正常退出,事务被自动提交
用户非正常退出,事务被自动回滚
系统崩溃,包括硬件或软件故障,事务被自动回滚

11.避免脏读
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库,这时,另外一个事务也在访问这个数据,然后使用了这个数据
例如:
里斯的年龄为20,事务A中把他的年龄改为21,但是事务A尚未提交;同时,事务B正在读取里斯的年龄,读取到里斯的年龄为21;随后事务A发生异常,而回滚了事务,里斯的年龄又回滚回了20;最后事务B读取到的里斯年龄为21的数据即为脏数据,事务做了一次脏读

事实上,Oracle目前不支持脏读,对于未提交的数据只能取到原始数据,没有提交的更新不能出现在用户的查询结果中
Oracle在进行并发控制时,为了保证数据的一致性和完整性不支持脏读,但也降低了部分并发性能

12.避免不可重复读
不可重复读,是指在一个事务内,多次读同一数据;然后在这个事务还没有结束的时候,另外一个事务也访问该同一数据;那么在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的,这是不允许的
例如:
里斯的年龄为20,事务A第一次读取时该值为20;随后,事务B对里斯的年龄进行了修改,将其值改为21并提交;最后,事务A再一次读取到里斯的年龄,此时的值就变为21了;事务A两次对里斯年龄的读取不同,构成了不可重复读

select * from stu where sname = '里斯' for update;
update stu set sage = 21 where sname = '里斯';
第一个执行的时候加上for update表示为该语句查询到的记录加上锁,其他事务不能再对该记录进行update操作,因此执行第二个操作update的时候被卡住,呈现假死状态

从程序员的角度看,Oracle提供的锁机制分为乐观锁和悲观锁;Oracle的悲观锁分成两种方式,从SQL语句的区别来看,就是for update和for update nowait两种形式

Oracle用的是行级锁,只是对想要锁定的数据才进行锁定,其余的数据不相干,所以在对Oracle表中并发插数据的时候,基本上不会有任何影响

13.判断数据是否被锁
select * from stu where sname = '里斯' for update;
select * from stu where sname = '里斯' for update nowait;
通过带nowait参数的select语句检测当前操作的数据是否被锁住,当发现数据被别的session锁定的时候,就会迅速的返回ora-00054错误,不让用户对该数据进行update等操作,避免了假死的可能

14.锁定数据表
Oracle表级锁,对整个数据表进行锁定,不允许其他事务对该表进行任何操作
lock table stu in exclusive mode;

使用Oracle提供的lock table命令将一个基本表stu锁定了,表被锁定之后,其他事务不能再对该表进行DML操作,否则该事务将会出现假死状态,但对锁定表执行select等操作是可以的
参数in exclusive mode表示以独占(排他)模式锁定表,即该表之恶能由该事务来访问和操作,不允许其他事务对其进行操作

15.查看被锁定的数据对象
Oracle提供了试图V$LOCKED_OBJECT用于监控锁的运行状态
select oracle_username,object_id,session_id from v$locked_object;
select object_name,object_type from dba_objects where object_id = 55332;
第二个查询中的条件是第一个查询查出来的结果

16.为被锁定表解锁
查询被锁定对象的进程号:
select sid,serial#,program from v$session where sid = 148;
使用kill结束对应的进程:
alter system kill session '148,1009';
需要重复执行,直到视图v$locked_object中被锁定stu表所对应的session_id全部被结束为止

事实上,只要不是异常关闭数据库连接,锁定的数据对象会在重新连接后自动解锁

17.创建程序包
程序包是一组相关过程、函数、变量、常量和游标等PLSQL程序设计元素的组合,作为一个完整的单元存储在数据库中,用名称来标识包
create or replace package stu_pkg
is
  stu_rec stu%rowtype;
  procedure delete_stu(sno in number);
  procedure query_stu(sno in number);
end stu_pkg;

create or replace package body stu_pkg
is
procedure delete_stu(sno in number)
is
begin
  delete from stu where sno = sno;
  if sql%found then
    dbms_output.put_line('数据库中没有学号为'||sno||'的学生');
  else
    dbms_output.put_line('删除成功');
  end if;
exception
  when others then
    dbms_output.put_line('操作异常');
end delete_stu;
procedure query_stu(sno in number)
is
begin
  select * into stu_rec from stu where sno = sno;
exception
  when no_data_found then
    dbms_output.put_line('数据库没有学号为'||sno||'的学生');
  when too_many_rows then
    dbms_output.put_line('程序运行错误,请使用游标进行操作');
  when others then
    dbms_output.put_line(sqlcode || '----' || sqlerrm);
end query_stu;
begin
  null;
end stu_pkg;
包创建完成后,可以通过对数据字典all_source的查询操作查看其名称和类型

为了实现信息的隐藏,建议不要将所有组件都放在包说明处声明,只应该把公共组件放在包声明部分;此外,在包主体定义共有程序时,他们必须与包定义中所声明子程序的格式完全一致

18.使用程序包
set serveroutput on;
begin
  stu_pkg.query_stu('12001');
  dbms_output.put_line(stu_pkg.stu_rec.sno||'----'||stu_pkg.stu_rec.sname||'----'||stu_pkg.stu_rec.sage||'----'||stu.pkg.stu_rec.sdept);
end;
包体只能在包说明被创建或编译后才能进行创建或编译,在胞体中实现的过程、函数、游标的名称必须与包说明中的过程、函数、游标一致

19.查看程序包
select text from all_source where name = upper('stu_pkg');

20.删除程序包
可以选择只删除包体或者删除整个包;如果只删除包体,那么包定义仍然存储在数据库中,供用户定义其中的过程和函数;如果删除整个包,则该包所有的信息都从数据库中移除了
drop package body stu_pkg;
drop package stu_pkg;

在创建程序包时,头部和包体是分别创建的,并且头部必须在包体之前创建,而删除包时不需要遵循该规则,可以单独删除包体,也可以删除整个包,但不能只删除包定义,否则包体将变成无用的数据


 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值