目录
Day 12
一、索引
- 源代码都是字符串,字符串是计算机无法理解的内容,经过编译后变量名变成地址,常规的代码对数据的操作都是通过地址来访问空间,从而获得对应的数据
- 哈希算法:在一个有序的数据集中,找到所需的数值所在的地址。
- 插入的数据未必在表格的末尾,根据表的索引插入数据,表格有序
- 用delete可以回滚rollback
delete from student where 1=1
-- 每次只删一条数据,数据保存在历史记录中,可回退
-- 如果表数据较多,效率低,因为要保存在历史记录中
--高效删除整个表内数据
truncate table 表名
--实质为将所有数据设为非法,内存回收,无法回退
- 表结构对sage设定了顺序,这个就是所谓的索引,结构索引:聚集索引,聚合索引
delete from student where sid<10009 and sage>19
-- 这条删除语句在执行时,判断条件不是先看sid,而是看sage
-- 因为sage被设置为索引,找到满足sage条件的数据后再判断sid
- 如果表有主键,默认主键就是聚集索引
- 聚集索引最多只有一个,联合主键默认的索引也只有一个,而不是两个
- 创建非聚集索引,可以使查询时即有sid排序,也有sage排序
-- 表格默认sid主键为聚集索引,创建一个ssage的非聚集索引
create index idxSage on student(sage)
-- 这是上面这条语句创建的sage索引表,左边是地址,右边是sage
select rowid,s.sage from student s order by sage
- 聚集索引最多只能有一个,而非聚集索引可以有多个
- 方便了查询速度,但牺牲了空间,保存了多个索引表(空间换时间)
- 什么情况下对属性建立索引?查询最频繁的属性,性价比最高的
- 唯一索引:因为索引唯一,搜索速度优于非唯一索引
--唯一索引的前提是属性有unique约束
create unique index ... on student(sname)
二、视图
- 桌面快捷方式不保存任何数据,视图也不保存任何数据
- 视图的作用相当于查询表的快捷键
-- 数据库中权限控制的最小颗粒是表
select * from student;
-- 创建有三列属性的student表快速查询视图,相当于对外公开的属性只有这三列
create view vstu as
select sid,sname,ssex from student
-- 查看视图
select * from vstu
-- 通过视图实现分步骤查询
create view step1 as select sid,trunc(avg(cmark),2) amk, from mark group by sid;
select * from (step1);
- 利用视图模拟均分表,找出最高均分对应的学号
create view step1 as select sid,trunc(avg(cmark),2) amk, from mark group by sid
select sid from step1 where amk in(
select max(amk) from step1
);
- 找出姓名,课程名,成绩
create view step2 as select sname,cname,cmark from student s,course c,mark m
where s.sid=m.sid and c.cid=m.cid;
select * from step2;
- 视图能否进行增删改?
1、视图中的数据来自单表时可以增删改;
2、数据来自真实的数据,没有修改;
修改数据指类似trunc(amk,2)这样的函数操作。
- 往视图中插入数据错误范例
create table student(sid ... sname ... ssex char(3) not null);
create view stuv as select sid,sname from student;
insert into stuv(sid,sname) values(20001,'刘虎');
插入语句报错,原因是实际上插入语句不是将数据插入视图中,而是student表中,根据student表的ssex非空约束,插入失败
创造备份表和原表不同步,而视图永远和原表同步
对视图操作实际上是对表格操作
- 打印最后一天的日期,并且格式为 yyyy-mm-dd
select to_char(last_day(to_date('2015-3-18','yyyy-mm-dd')),'yyyy-mm-dd') from dual
- 现有学生表中显示每个学生的姓名和生日
select sname,add_months(sysdate,-12*sage) from student
- 创建一张学生表stu2,表中有姓名和生日
往表中插入两条数据
用查询语句获得学生姓名和年龄
create table stu2 (
sname varchar2(20),
birth date()
);
insert into stu2(sname,birth) values('张三',to_date('1997-11-05','yyyy-mm-dd'))
insert into stu2(sname,birth) values('李四',to_date('1997-10-06','yyyy-mm-dd'))
select sanme,(extract(year from sysdate) - extract(year from birth) + 1) sage from stu2
三、函数
时间
- 查询系统时间的两种格式
-- sysdate 内置函数可以获取当前的系统日期和时间,返回 DATE
SELECT SYSDATE FROM DUAL
-- systimestamp 函数可以返回当前日期、时间和时区
SELECT SYSTIMESTAMP FROM DUAL
- sysdate默认显示日月年,可以使用to_char显示时分秒
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual
- 指定日期和时间,并转换为DATE格式
TO_DATE('1909-08-09 06:30:10',' YYYY-MM-DD HH24:MI:SS')
-- 注意字符串和后面的格式要对应
- LAST_DAY(d),返回指定日期当月的最后一天。
select SYSDATE,LAST_DAY(SYSDATE) from dual
- 计算出’2015-3-18’这一天所在月的最后一天,并且以 yyyy-mm-dd的格式打印
select to_char(last_day(to_date('2015-03-18','yyyy-mm-dd')),'yyyy-mm-dd') from dual
- EXTRACT(fmt FROM d),提取日期中的特定部分。
fmt 为: YEAR、 MONTH、 DAY、 HOUR、 MINUTE、 SECOND。其中 YEAR、 MONTH、 DAY
可以为 DATE 类型匹配,也可以与 TIMESTAMP 类型匹配;但是 HOUR、 MINUTE、 SECOND 必
须与 TIMESTAMP 类型匹配
select SYSDATE 时间,
EXTRACT(YEAR from SYSDATE) 年,
EXTRACT(MONTH from SYSDATE) 月,
EXTRACT(DAY from SYSDATE) 日,
EXTRACT(HOUR from SYSTIMESTAMP) 时,
EXTRACT(MINUTE from SYSTIMESTAMP) 分,
EXTRACT(SECOND from SYSTIMESTAMP) 秒,
from dual
- 现有的学生表中显示每个学生的姓名和生日
select sname,to_char(add_months(sysdate,-12*sage),'yyyy-mm-dd') from student
- 创建一张学生表stu2,这张表中有姓名和生日
向这张表中插入两条数据
用查询语句获得学生的姓名和年龄
create talbe stu2(
sname varchar(50);
birth date;
)
insert into stu2(sname,birth) values('张三',to_date('1997-11-05','yyyy-mm-dd'));
insert into stu2(sname,birth) values('李四',to_date('1998-04-03','yyyy-mm-dd'));
select sname,(extract(year from sysdate)-extract(year from birth) + 1) from stu2
数字函数
- 数字函数接受数字参数,参数可以来自表中的一列,也可以是一个数字表达式。
ABS(x) x 绝对值 ABS(-3)=3
ACOS(x) x 的反余弦 ACOS(1)=0
COS(x) 余弦 COS(1)=1.57079633
CEIL(x) 大于或等于 x 的最小值 CEIL(5.4)=6
FLOOR(x) 小于或等于 x 的最大值 FLOOR(5.8)=5
LOG(x,y) x 为底 y 的对数 LOG(2,4)=2
MOD(x,y) x 除以 y 的余数 MOD(8,3)=2
POWER(x,y) x 的 y 次幂 POWER(2,3)=8
SQRT(x) x 的平方根 SQRT(4)=2
ROUND(x,y) x 在第 y 位四舍五入 ROUND(3.456,2)=3.46
ROUND(x) 相当于默认y=0,在个位四舍五入
y 是正整数,就是四舍五入到小数点后 y 位。 ROUND(5.654,2)=5.65
y 是负整数,四舍五入到小数点左边|y|位。 ROUND(351.654,-2)=400
TRUNC(x,y) x 在第 y 位截断 TRUNC(3.456,2)=3.45
TRUNC(x) 在缺省 y 时,默认 y=0;比如: TRUNC (3.56)=3
y 是正整数,就是四舍五入到小数点后 y 位。 TRUNC (5.654,2)=5.65
y 是负整数,四舍五入到小数点左边|y|位。 TRUNC (351.654,-2)=300
字符串
- ascii(‘x’) 返回字符x在ascii中的编码
-- 打印 0 a A 汉 的ascii编码
select ascii('0'),ascii('a'),ascii('A'),ascii('汉') from dual
-- 参数只能是一个字符,多个时只计算第一个字符的ascii编码
- 字符串连接符 ||
-- concat('ab','cd') 也是字符串连接,但||更常用
select '大家好,我的名字是'||sname||',我来自'||sanativeplace||',我今年'||sage||'岁' from student
- 找子串所在位置
select instr('hello world','ell') from dual
-- 返回结果为2,和java、c语言不同,返回0代表未找到
select instr('hello world','ell',0) from dual
-- 也可以人为设置起点为0,返回-1代表未找到
- 计算字符串长度
select length('abc') from dual
- 字符串大小写
insert into student(sid,sname) values(11111,'Abc')
--找不到上面这条插入的数据,因为字符串没有匹配→区分大小写
select * from student where sname='abc'
-- 可以在插入数据时用upper/lower函数将字符串转化为大/小写
- 修剪字符串
insert into student(sid,sname) values(11111,trim(' AB C '))
-- 得到的结果为 AB C,即去除了左右两侧的空格
-- 类似的还有LTRIM和RTRIM去除左/右的空格
- 字符串截取获得子串
select substr('abcdefghi',2,3) from dual
--返回bcd
序列
- 用来产生连续的数字,序列常常用来作为主键中增长列。
- 序列中的可以升序生成,也可以降序生成。
create sequence sequence_name
[start with num] --从num这个数字开始生成
[INCREMENT BY increment] --每次增长increment的增量
[MAXVALUE num|NOMAXVALUE] --设置最大值
[MINVALUE num|NOMINVALUE] --设置最小值
[CYCLE|NOCYCLE] --默认不循环
[CACHE num|NOCACHE] --预生成一组序列,提高效率
create sequence sq_sid
start with 2000 increment by 2;
select sq_sid.nextval from dual; --用序列生成序号,此时为2000
四、程序
数据库的代码结构
set serveroutput on
declare
--变元定义,这里的内容都是编译阶段的代码,不能在这赋值,可以赋初值
begin
--代码体,语句体,这里都是在运行阶段执行的内容
dbms_output.pin_line('hello world!'); --需要提前开启打印开关
exception
--异常处理
end;
- 程序只能脚本输出,不能查询输出
- 赋值和等号
c java =叫做赋值号 ==叫等号
数据库 =叫等号 :=叫赋值号
- 程序提示用户输入学生姓名
程序根据学生姓名找到学生的年龄
set serveroutput on
declare
name varchar2():='&请输入您的姓名';
age number;
begin
select sage into age from student where sname=name;
dbms_output.put_line(name||'的年龄为'||age||'岁');
end;
- 程序提示用户输入学生姓名
程序根据学生姓名找到学生的最高分,均分和总分
程序打印“XX的总分为…均分为…最高分为…”
set serveroutput on
declare
age number;
name varchar2():='&请输入您的姓名';
avg number;
sum number;
max number;
begin
select sage into age from student where sname=name;
select trunc(avg(cmark),2),max(cmark),sum(cmark) into avg,max,sum
from mark where sid=(
select sid from student where sname=name
) group by sid;
dbms_output.put_line(name||'的均分为'||avg||',总分为'||sum||'最高分为'||max);
end;
- 程序提示用户输入学生姓名
程序根据学生姓名找到学生的均分,并判断成绩情况
set serveroutput on
declare
nm varchar2():='&请输入学生姓名';
amk number(5,2);
begin
select avg(cmark) into amk from mark
where sid=(
select sid from student where sname=nm
) group by sid;
if amk>=90 then
dbms_output.put_line(nm||'的成绩优秀:'||amk);
elsif amk>=70 then
dbms_output.put_line(nm||'的成绩普通:'||amk);
else
dbms_output.put_line(nm||'的成绩较差:'||amk);
end if
end;
- 程序提示输入学生姓名,程序校验学生最高分和最低分,如果两个分数差值超过15分,提示学生存在严重偏科,XX课程急需提升;如果分数差值在10分以内,提示学生学习非常稳定和均衡,否则提示学生的XX科目还有提升空间。
set serveroutput on
declare
nm varchar2(100):='&请输入学生姓名';
max number(5,2);
min number(5,2);
cnmin varchar2(200);
begin
select max(cmark),min(cmark) into max,min from mark where sid=(
select sid from student where sname=nm
) group by sid;
select cname into cnmin from course where cid=(
select cid from mark where cmark=min and sid=(
select sid from student where sname=nm
)
)
if max-min > 15 then
dbms_output.put_line(nm||'偏科严重,'||cnmin||'急需提升');
elsif max-min < 10 then
dbms_output.put_line(nm||'学习非常稳定和均衡');
else
dbms_output.put_line(nm||'的'||cnmin||'还有提升空间');
end if
end;
- 数据类型保持一致就可以忽略(自适应数据类型:变量名 表名.属性名%type)
set serveroutput on
declare
nm varchar2(100):='&姓名';
age student.sage%type;
begin
select sage into age from student where sname=nm;
dbms_output.put_line(age);
end;
- 变量名 表名%rowtype 存储一整条数据,通过变量名.属性名获得属性值
set serveroutput on
declare
line student%rowtype;
begin
select * into line from student where sname='李四'
dbms_output.put_line(line.sname||'的年龄为'||line.sage);
end;
- 请编写代码打印出年龄最大的一名学生的姓名,年龄和性别
set serveroutput on
declare
line student%rowtype;
begin
select * into line from (
select * from student order by sage desc
) where rownum=1;
dbms_output.put_line(
line.sname||',年龄为'||line.sage||'岁,性别为:'||line.ssex
);
end;
- while循环 1-100累加
set serveroutput on
declare
i int;
s int;
begin
while i<=100 loop
s:=s+i;
i:=i+1;
dbms_output.put_line(s);
end loop;
end;
- for循环 1-100累加
set serveroutput on
declare
i int;
s int:=0;
begin
for i in 1..100 loop
s:=s+i;
end loop;
dbms_output.put_line(s);
end;
- 打印所有男生年龄
set serveroutput on
declare
s student%rowtype;
begin
for s in (select * from student where ssex='男') loop
dbms_output.put_line(s.sname||'今年'||s.sage||'岁');
end loop;
end;
- 请分别打印数学成绩在90分以上的学生姓名
-- 我的方法
set serveroutput on
declare
m mark%rowtype;
sn varchar2(200);
begin
for m in (
select * from mark
where cid=(select cid from course where cname='数学') and cmark>90
) loop
select sname into sn from student where sid=m.sid;
dbms_output.put_line(sn||'的数学成绩为'||m.cmark);
end loop;
end;
-- 视图方法
create view mlmk as select sname,cmark from student s join mark m on s.sid=m.sid
where cid=(select cid from course where cname='数学') and cmark>=90
set serveroutput on
declare
s mlmk%rowtype;
begin
for s in (select * from mlmk) loop
dbms_output.put_line(s.sname||'的数学成绩为'||s.cmark);
end loop;
end;
- 扫描每个男生的均分,并打印 某某的均分为XX,成绩[优秀,普通,差]
create view ag as select s.sid,s.sname,trunc(avg(cmark),2) amk
from mark m,student s
where ssex='男' and m.sid=s.sid group by s.sid,s.sname;
set serveroutput on
declare
a ag%rowtype;
begin
for a in (select * from ag) loop
if a.amk>=90 then
dbms_output.put_line(a.sname||'的均分为:'||a.amk||',成绩优秀');
elsif a.amk>=70 then
dbms_output.put_line(a.sname||'的均分为:'||a.amk||',成绩普通');
else
dbms_output.put_line(a.sname||'的均分为:'||a.amk||',成绩差');
end if;
end loop;
end;
异常处理
- 异常错误有三种类型:预定义错误、非预定义错误、用户定义错误;
- 出现错误进入异常处理之后停止代码运行
- 预定义的异常处理
- 异常处理结构举例
set serveroutput on
declare
nm student.sname%type:='&请输入学生姓名';
age student.sage%type;
begin
select sage into age from student where sname=nm;
dems_output.put_line(nm||'的年龄为:'||age);
exception
when No_data_found then
dbms_output.put_line('学生表中没有姓名为'||nm||'的学生');
when others then
dbms_output.put_line('其他类型异常');
end;
- 输入学生姓名,找出这个学生的最高分以及对应的课程名
显示 XX的最高分为…,课程是…
需要注意:学生可能不存在,也可能没有选课
还有一种可能是这个最高分对应的课程不止一门,请将所有对应的最高分课程都显示出来
set serveroutput on
declare
nm student.sname%type:='&请输入学生姓名';
sno student.sid%type;
mx mark.cmark%type;
line mark%rowtype;
cnm course.cname%type;
str varchar2(100);
begin
select sid into sno from student where sname=nm;
select max(cmark) into mx from mark where sid=sno;
str:=nm||'的最高分为:'||mx||',对应的课程是:';
for line in (select * from mark where sid=sno and cmark=mx) loop
select cname into cnm from course where cid=line.cid;
dbms_output.put_line(str||cnm);
end loop;
exception
when No_data_found then
dbms_output.put_line('表中没有姓名为'||nm||'的学生,或学生未选课');
end;
- 抛出用户自定义异常
create table stu as select * from student;
set serveroutput on
declare
sexerr exception;
sex student.ssex%type:='&请输入性别';
begin
if sex not in('男','女') then
raise sexerr;
end if;
insert into stu(ssex) value(sex);
exception
when sexerr then
dbms_output.put_line('您输入的性别非法,请输入正确的性别:男或女');
end;
- 假设学校中对学生的要求是学分必须达到15分才算合格
程序提示用户输入学生学号
那么如果这个学生合格,则输出此人的均分
如果学生学分不足,则异常:“学生学分不足,当前学分为…”
set serveroutput on
declare
cvalerr exception;
sno student.sid%type:='&请输入学生的学号';
nm student.sname%type;
sumcval course.cval%type;
amk mark.cmark%type;
begin
select sum(cvar) into sumcval from mark m join course c on m.cid=c.cid where m.sid=sno;
select sname into nm from student where sid=sno;
if sumcval<15 then
raise cvalerr;
end if;
select avg(cmark) into amk from mark where sid=sno;
dbms_output.put_line(nm||'的均分为'||amk);
exception
when cvalerr then
dbms_output.put_line(nm||'学分不足,当前学分为'||sumcval);
when no_data_found then
dbms_output.put_line('没有这个人或者此人没有选课');
end;
存储过程
- 服务器里有DBMS(数据库管理系统)和DB(数据库),两者有直接数据通道;客户端向DBMS下达指令(字符串),通过网络加密通信;DBMS将指令(字符串)用DBMS内的编译器编译成计算机能直接识别的二进制码,再由DBMS执行,最后结果返回给客户端。(结果返回给客户端后,内存中的二进制码指令会被删除)
- 网络延时、编译、查询等耗费时间多。
- 将编译后的指令保存,方便下次调用,即所谓存储过程。
- 范例,带参数的存储过程(形参中的字符串定义不可以设定其长度)
-- 生成[ 过程 ],仅在服务器编译,不执行
create procedure abc(nm varchar2)
as
amk mark.cmark%type;
begin
select avg(cmark) into amk from mark where sid=(
select sid from student where sname=nm
);
dbms_output.put_line(nm||'的均分为'||amk);
end;
-- 执行
set serveroutput on
begin
abc('张三')
end;
- 优点:减少代码的量,缩短网络延时;共享代码,其他客户端也能调用
- 缺点:占用内存,过程指令以二进制方式存储在服务器侧
- 无参存储过程
create or replace procedure getAllAvg
as
amk number;
begin
select avg(cmark) into amk from mark;
dbms_output.put_line(amk);
end;
set serveroutput on
begin
getAllAvg();
end;
- 删除存储过程
drop procedure [过程名]
- 更优解决方法
create or replace procedure XXX
as
···
begin
···
end;
- 编写存储过程,传入姓名和课程名,打印学生该课程成绩
添加异常处理,如果传入的姓名或者课程名不存在,则进行异常提示
create or replace procedure getCmark(nm varchar2, cn varchar2)
as
cm mark.cmark%type;
begin
select cmark into cm from mark where sid=(
select sid from student where sname=nm
) and cid=(
select cid from course where cname=cn
);
dbms_output.put_line(cm);
exception
when no_data_found then
dbms_output.put_line('学生表中不存在姓名为'||nm||'的学生'||'或课程表中不存在名为'||cn||'的课程');
when others then
dbms_output.put_line('其他异常');
end;
- 存储过程有return语句,但返回值有范围:-127~126,仅用于返回运行状态;
- 如果要返回其他类型值,应放到形参中。
形参默认in类型,out类型的形参即输出参数
create or replace procedure abc(nm in varchar2, amk out number)
as
begin
select avg(cmark) into amk from mark where sid=(
select sid from student where sname=nm
);
end;
- 调用输出类型的形参
set serveroutput on
declare
k number;
begin
abc('张三',k);
dbms_output.put_line(k);
end;
- 输入姓名,课程名,输出成绩
create or replace procedure getCmark(nm varchar2, cn varchar2, cm out number)
as
begin
select cmark into cm from mark where sid=(
select sid from student where sname=nm
) and cid=(
select cid from course where cname=cn
);
end;
declare
t number
begin
getMark('张三','数学',t);
dbms_output.put_line(t);
end;
- 存储过程中可以嵌套存储过程
-- 标准:
execute 过程名( ...形参...);
-- execute可省略
函数
- 函数和存储过程的区别在于,存储过程的返回为一个状态,函数能返回一个值
crete function abc(i number,j number)
as
begin
return
end;
- 多数情况使用存储过程,存储过程的返回值放形参输出
五、事务
- 在数据库中事务是工作的逻辑单元,一个事务是由一个或多个完成一组的相关行为的SQL语句组成,通过事务机制确保这一组SQL语句所作的操作要么都成功执行,完成整个工作单元操作,要么一个也不执行。
- 事务是通过锁实现的,锁是通过信号量实现的。
1、事务特性
- 原子性(Atomicity):一个事务里面所有包含的SQL语句是一个执行整体,不可分割,要么都做,要么都不做。
- 一致性(Consistency):事务开始时,数据库中的数据是一致的,事务结束时,数据库的数据也应该是一致的。
- 隔离性(Isolation):是指数据库允许多个并发事务同时对其中的数据进行读写和修改的能力,隔离性可以防止事务的并发执行时,由于他们的操作命令交叉执行而导致的数据不一致状态。
- 持久性 (Durability) : 是指当事务结束后,它对数据库中的影响是永久的,即便系统遇到故障的情况下,数据也不会丢失。
2、数据异常
因为Oracle中支持多个事务并发执行,所以会出现下面的数据异常。
(1)脏读
当一个事务修改数据时,另一事务读取了该数据,但是第一个事务由于某种原因取消对数据修改,使数据返回了原状态,这是第二个事务读取的数据与数据库中数据不一致,这就叫脏读。
如:事务T1修改了一条数据,但是还未提交,事务T2恰好读取到了这条修改后了的数据,此时T1将事务回滚,这个时候T2读取到的数据就是脏数据。
(2)不可重复读
是指一个事务读取数据库中的数据后,另一个事务则更新了数据,当第一个事务再次读取其中的数据时,就会发现数据已经发生了改变,这就是不可重复读取。不可重复读取所导致的结果就是一个事务前后两次读取的数据不相同。
如:事务T1读取一行记录,紧接着事务T2修改了T1刚刚读取的记录,然后T1再次查询,发现与第一次读取的记录不同。
(3)幻读
如果一个事务基于某个条件读取数据后,另一个事务则更新了同一个表中的数据,这时第一个事务再次读取数据时,根据搜索的条件返回了不同的行,这就是幻读。
如:事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,恰好满足T1的where条件。然后T1使用相同的条件再次查询,结果集中可以看到T2插入的记录,这条新纪录就是幻读。
事务中遇到的这些异常与事务的隔离性设置有关,事务的隔离性设置越多,异常就出现的越少,但并发效果就越低,事务的隔离性设置越少,异常出现的越多,并发效果越高。
Oracle实例
- Oracle每次用户尝试对数据改动的时候,这个改动不会立即写入硬盘,因为一旦写入硬盘,这个事务就完成了。
- 所以每次数据的改动会首先写入服务器的内存中为用户开辟的缓存里,而当用户改动完数据后,如果用户“确认”这次的改动无误,就会“提交”操作,提交的本质就是写硬盘。
- 如果用户认为改动不合理,可以“回滚”操作,所谓“回滚”就是放弃将缓存中的改动写入硬盘,并清理掉这次事务的缓存。
--提交
commit;
--回滚
rollback;
- 并非一个窗口一个缓存,而是一个连接一个缓存
缓存是面向连接的,不同连接,同一个数据库的实例的客户端必须通过commit或rollback来同步
回滚事务
- 保存点(savepoint):是事务中的一点,用于取消部分事务,当结束事务时,会自动的删除该事务所定义的所有保存点。当执行ROLLBACK时,通过指定保存点可以回退到指定的点。
- 设置保存点:
Savepoint a;
- 删除保存点:
Release Savepoint a;
- 回滚部分事务:
Rollback To a;
- 回滚全部事务:
Rollback;
- 事务一旦结束,保存点会自动删除,因为提交之后,缓存也清空了,回滚无效。
死锁
- 数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。
- 在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改;加了共享锁的数据对象可以被其他事务读取,但不能修改。