数据库
数据库技术,是20世纪60年代开始兴起的一门信息管理自动化的学科,是计算机科学中的一个重要分
支。数据管理是数据库的核心任务,内容包括对数据的分类、组织、编码、储存、检索和维护。
从数据管理的角度看,数据库技术到目前共经历了以下几个阶段:
- 人工管理阶段
- 文件系统阶段
- 数据库系统阶段
- 采用复杂的结构化的数据模型:数据库系统不仅要描述数据本身,还要描述数据之间的联系。这种联系是通过存取路径来实现的。
- 较高的数据独立性:数据和程序彼此独立,数据存储结构的变化尽量不影响用户程序的使用。
- 最低的冗余度:数据库系统中的重复数据被减少到最低程度,这样,在有限的存储空间内可以存放更多的数据。
- 数据控制功能:数据库系统具有数据的安全性,以防止数据的丢失和被非法使用;具有数据的完整性,以保护数据的正确、有效和相容;具有数据的并发控制,避免并发程序之间的相互干扰;具有数据的恢复功能,在数据库被破坏或数据不可靠时,系统有能力把数据库恢复到最近某个时刻的正确状态。
数据库对象是关系型数据库的组成部分,常常用CREATE命令进行创建,使用ALTER命令修改,用DROP执行删除操作。常见的数据库对象有:
- 用户(user)
- 表(table)
- 视图(view)
- 索引(index)
- 触发器(trigger)
- 存储过程(procedure)
- 同义词(synonym)
- 序列(sequence)(仅Oracle)
对关系型数据库对象进行操作的语言被称为结构化查询语言(Structured Query Language)。SQL语句的分类:
- DQL(Data Query Language),数据查询语言:用于检索数据库中的数据,主要是seletct语句
- DML(Data Manipulation Language),数据操纵语言:用于改变数据库中的数据,主要是insert、update、delete语句
- DDL(Data Define Langage),数据定义语言:用来建立、修改、删除数据库对象,主要是create、alter、drop、truncat语句
- TCL(Transaction Control Language),事务控制语言:用于维护数据的一致性,主要是commit、rollback、savepoint语句
- DCL(Data Control Language),数据控制语言:用于执行权限授予和权限收回操作,主要是grant、revote语句
在基础的SQL之上,针对不同的数据库诞生了许多不同的SQL,如PL/SQL(程序化的SQL语句,在其基础上加入一定的逻辑操作,如if、for等)。
现在流行的数据库软件包括MySQL、Oracle、SQL Server、SQLite(安卓采用的数据库)、MariaDB(MySQL的替代版本)、MongoDB(分布式文件存储的非关系型数据库)等。
Oracle
Oracle数据库是ORACLE(甲骨文)公司的核心软件产品。作为企业级数据库的主打产品,Oracle数据库在众多关系型数据库之中,表现突出,性能优越。但是Oracle数据库的价格十分昂贵。
Oracle数据库安装成功后,Oracle会启动OracleServiceXE和OracleXETNSListener服务。前者是Oracle的核心服务(SQLplus就依赖于此),后者是Oracle的数据库管理服务(数据库登录、JDBC都依赖于此)。XE是数据库的名字。
Oracle自带一个浏览器端的管理系统。其登录地址为: http://127.0.0.1:8080/apex/。SQLplus是Oracle数据库自带的连接到数据库进行操作的工具。
设计、DDL
DDL即数据定义语言,主要用于定义、修改和删除表、序列、视图和索引等数据库对象。在构建这些对象之前,首先还是先要设计一个数据库。
设计数据库
数据库软件亦遵循一般软件的开发流程。软件开发流程,即软件的设计思路和方法的一般过程,包括:
分析、设计、编码、测试、部署、上线、运维
这里每一项又可以细分为多个环节,例如分析可分为可行性分析、需求分析等,设计可以分为总体设计、模块设计、数据库设计等。
数据建模
在需求分析过程中,其中一项重要的工作是为系统进行建模,它为后续的设计和实现工作提供了支持。数据建模主要是要抽象出系统中所涉及到的实体,以及它们之间的关系。此过程一般需要经过三个阶段:
- 概念建模:此阶段需要与客户交流、理解需求、形成实体,这一阶段需要与客户进行反复沟通、理解和确认,是一个迭代的过程。
- 逻辑建模:此阶段主要是对概念建模阶段的实体进行细化、优化、测试等,并最后形成具体的E-R图(实体-关系图)详细记录实体信息和之间的关系。
- 物理建模:此阶段中将根据形成的E-R图选择相应合理的数据库,并编写SQL代码和创建数据库对象,进行前期的功能测试、数据测试,以便能及时发现问题并处理。可能还需要考虑表拆分、读写分离、高并发和主从复制(分布式)等。
实体、属性和关系
- 实体(Entity):用来表示据有相同特征和性质的事物,由实体名和实体属性组成。相当于Java中的类,在数据库中即一张表。
- 属性(Property):实体所具有的特征即它的属性,一个实体可以拥有多个属性。属性在设计时要考虑其是否全面、是否唯一、是否为空、是否拥有约束。
- 关系(Relationship):实体之间可以具有关系。实体关系主要包括:一对一关系(1:1)、一对多关系(1:n)和多对多关系(n:n)。关系也可以具有属性。属性之间也可以具有关系,也就是外键。设置外键的属性如果可以为空,则是may be关系,否则是must be关系。
E-R图
E-R图即实体-关系图,能够直观表示实体和实体之间的关系,也可以表示实体中的属性。

- 在E-R图中,实体使用矩形或圆角矩形表示;关系使用菱形表示;属性使用圆形表示,也可以直接写在实体中。
- 使用连线表示关系,其中伞状的一端表示“多”的一方,否则是“一”的一方,虚线表示may be关系,实线表示must be关系,连线上面还可以加上文字说明。
- 针对属性,可以使用#或PK或🔑表示唯一属性,*或NN表示非空,o表示可以为空。
- 使用不同的工具绘制的E-R图会有一些区别。
数据库设计
数据建模完成之后,可以把E-R图转换成数据库中的表:
- 实体名转换为表名。
- 属性转换为列(字段)。
- 唯一属性设置为表中的主键或唯一键。
- 根据实体之间的关系,可以设置外键列。
建立的表要满足一定的规范:
- 主键:即表中每条记录的唯一标识。可以让多个列联合在一起做表中的主键,此时该主键被称为联合主键。主键列不允许为空。
- 外键:用于标识某一字段(列)的数据是从另一张表引用的。被引用的字段必须具有唯一键(可以为空)或作为主键。对于一对一关系,外键可以设置在任何一方;对于一对多关系,外键设置在多的一方;对于多对多关系,需要设计第三张表(桥表),在桥表中设置两个外键,分别引用两张表的主键。
- 范式:具体实践中,主要用到前三个范式:列不可分割、直接或间接依赖主键。完全依照范式会造成一些常用查询经常进行多表连接,效率慢;不依照范式会造成数据冗余。
- 数据库对象的名字:由字母、数字、_、#组成,必须由字母开头,不重复、非关键字、不区分大小写、最长30个字符。
表
建立一个表的一般方法为:
create table 表名(
列名 数据类型 [constraint 约束名] [列级约束],
列名 数据类型 [constraint 约束名] [列级约束] [on delete],
[constraint 约束名] [表级约束],
[constraint 约束名] [表级约束]
);
数据类型
- 字符串类型:char、varcha、varchar2
- char(n),存储数据长度为n的字符串,长度固定且效率高。
- varchar,是数据库标准类型。可以存空字符串(’’)。
- varchar2(n),存储数据的长度是可变的,最小为n,最大为255。不能存空字符串,但可以存null。是Oracle特有的数据类型。
- 数值类型:number(p,s) - s表示保留的小数位(四舍五入),整数位为p-s,若没有参数则没有限制。
- 日期类型:date
- 二进制对象:blob - 可以存储视频,音频,图片等。但这些一般使用非关系型数据库存储。
- 大型文本:clob - 如XML等,也可以使用非关系型数据库存储。
约束
若要对存入的数据进行限制,则需要对表或列建立约束。约束分为以下几点:
- 主键(primary key):非空唯一键,每条记录的唯一标识。默认索引。
- 外键(foreign key):该列的数据需要引用另一张表某一(几)列的数据。可以为空。
- 引入外键的表,在参照的表中数据被删除时会出现一些问题。可以使用on delete关键字约定在参照表的数据被删除后,该列的值应该如何变化。有三种约定:on delete no action/cascade/set null,分别表示不做变化/级联删除/置空。
- 唯一键(unique):该列的数据不可重复(可以为空)。
- 非空(not null):该列的数据不可为空。
- 值限制(check):该列的值被限制在一张列表内。
以下是列级约束和表级约束的例子:
-- 列级约束
create table student(
id number constraint stu_id_pk primary key, -- 给约束起名为stu_id_pk
name varchar2(100) not null,
email varchar2(100) unique,
gender char(1) check(gender in('f','m')),
age number,
birthday date references birth_table(days)
);
-- 表级约束
create table student(
id number,
name varchar2(20) not null,
age number default 20, -- 默认值为20
email varchar2(100),
gender char,
age number,
birthday date,
constraint stu_id_pk primary key(id), -- 给约束起名为stu_id_pk
unique(email),
check(gender in('f','m')),
foreign key(birthday) references birth_table(days)
);
除了非空约束只在列级约束中声明外,其它约束均可使用表级约束来声明。
- 对于联合主键、联合外键、联合唯一键的声明,必须要使用表级约束。
- 建表的时候可以给约束起一个名字,一般为“表名_列名_约束类型缩写”。起名的关键字为constraint。如果没有起名,也会有一个默认约束名。可以根据约束名进行一些操作。
导入表
如果想从其它表导入一个表,或者说,通过DQL语句将查询获得的结果建立成为一个新的表,可以使用as关键字。
create table 表名
as
DQL语句;
修改和删除
可以使用alter对表的名字、列和约束进行修改。可以使用drop对表及表的列和约束进行删除。
alter table t_user
add birthday date -- 添加列
drop column birthday -- 删除列
add constraint user_name_un unique(name) -- 添加约束
disable constraint user_name_un cascade -- 禁用约束
enable constraint user_name_un -- 恢复约束
drop constraint user_name_un -- 删除约束
modify (name varchar2(500)); -- 修改列的数据类型
truncate table t_user; -- 截断表的数据,但保留表结构(即删除全部数据)
rename t_user to mytest; -- 重命名表
drop table mytest; -- 删除表
非表对象
视图
视图是表中数据的映射。视图可以隐藏表的一些数据,提高安全性。视图分为两类:
- 简单视图:对表进行简单的映射。可以通过简单视图对被映射的表中数据进行修改。
- 复杂视图:包含了group by、组函数或多表查询的视图。可以对复杂视图进行修改,但是所有的修改均不能应用到原表中。注:简单的多表连接应当理解为简单视图,修改这样的视图可以影响到表。
grant create view to briup; -- 创建视图需要获取create view权限
create or replace view 视图名 -- 创建视图
as
DQL语句
[with read only|with check opion];
drop view 视图名; -- 删除视图
可以为视图添加限制。with read only关键字使得该视图仅供查询,不可被修改。with check opion关键字使得只能通过该视图添加、修改和删除视图中可见的部分。
索引
索引能够提高数据库的查询速度。索引能够在查询时直接指向包含所查询值的行的位置,减少磁盘I/O。索引和表是相互独立的物理结构。数据库系统会自动使用、维护和更新索引。
当然,对于较小的表,是否建立索引对查询速度的提升并不明显。故即使创建了索引,如果查询速度很快,数据库系统也不会使用索引。索引也不是越多越好。
数据库系统会为唯一键(包括主键)自动创建索引。当然,也可以对索引进行手动创建和删除:
create index 索引名 -- 创建索引
on 表名(列名);
select 索引名 from user_indexes; -- 查询当前用户的索引
drop index 索引名; -- 删除索引
什么时候应该使用索引?
- 列包含的数据量很大,并且很多非空的值。
- 列经常作为where子句的限定条件或连接条件。
- 对于经常需要order by、group by或distinct的列,可以利用索引加快排序查询时间。
- 建立索引的表不应经常进行修改操作。
常见的索引种类包括:唯一值索引(unique index)、非唯一值索引(nounique index)、单行索引(single index)和多行索引(concatenated index)。
索引的常见结构有:
- B-tree:是一种树形结构,适合大量的增、删、改和唯一值较多的列,但不能使用包含or操作符的查询。这是默认创建的索引的结构。
- 位图:适合数据基数较小的表,非常适合OR操作符的查询。但每次操作都需要更新索引,更新操作开销大。
- 反序:是B-tree索引的一个分支,会对索引列的索引键值进行字节反转。它可以将索引键值分散到不用的节点中。
- 函数:针对具有函数操作的列的索引。
select * from v$option Where PARAMETER='Bit-mapped indexes'; -- 在Oracle中查询是否支持位图索引
create bitmap index bitmap_index on s_emp(last_name); -- 创建一个位图索引
create index emp_index_reverse on s_emp(last_name) reverse; -- 创建一个反序索引
create index func_index on s_emp(upper(last_name)); -- 创建一个函数索引
注释
在表创建完成后,可以使用comment为表或者列添加注释(说明)。可以通过DQL对注释进行查询。
comment on table t_user is '用户表'; -- 为表添加注释
comment on column t_user.name is '名字'; -- 为列添加注释
select * -- 查询表注释
from user_tab_comments
where table_name = upper('t_user');
select * -- 查询列注释
from user_col_comments
where comments is not null and
table_name = upper('t_user');
序列
序列是一种只在Oracle中出现数据库对象,它可以为主键添加自增数值。
create sequence 序列名
[increment by n] -- 步长,默认为1
[start with n] -- 初值,默认为1
[maxvalue n | nomaxvalue] -- 序列的最大值
[minvalue n | nominvalue] -- 序列的最小值
[cycle | nocycle] -- 序列到达最大值后是否循环
[cache n | nocache]; -- 序列的缓存区大小,默认为20
select 序列名.currval from dual; -- 查看序列当前值
select 序列名.nextval from dual; -- 查看序列下一个值
drop sequence test_seq; -- 删除序列
insert into t_user(id,name,salary) values(t_user_seq.nextval,'tom',2000); -- 使用序列插入新的行
DQL
DQL即数据查询语言。select是SQL的查询关键字。查询语句的结构为(数值为执行顺序):
select [distinct] 列 [|| 列2] [as 别名] --5
from 表 [别名] --1
[[where|on] 条件 --2
group by 列 --3
having 组条件 --4
order by 列 [asc|desc]]; --6
- 分隔:列与列、表与表之间,使用,分隔;表和列之间,用.分隔;条件和条件之间,用and或or分隔。
- 运算:列的数值可以使用±*/%进行运算,列的字符可以使用||进行拼接,还可以使用函数对列进行处理。
- 字符串:字符串使用’括起来;只有别名可以使用双引号或不使用引号。
- 别名:用as关键字可以给列起别名,用“表 别名”可以为表起暂时的别名。
- 去重:可以在select之后使用distinct对结果去重。要查询的记录每列完全相同才算重复数据。
- 格式化:format能够调整字符列的显示宽度。
column last_name format a15; -- last_name列宽15个字符
col last_name for a15; -- 简写
clear column; -- 清除格式
clear col;
- 条件:
-
、>=、=、<=、<、!=、<>、^=:比较操作符(后三个都是不等于)
- between a and b:在[a, b]区间内
- in(a, b, c, …):在列举的列表中
- like:模糊条件,可以使用通配符%(0~n个字符)和_(1个字符),以及使用escape定义转义字符
- is null/is not null:空值的判断(高开销的运算)
-
- 分组:使用group by可以将该列中相同的值归为一组。如果有多个列,则把这些列中组合完全相同的记录归为一组。默认将一个表的全部的记录组成一个组。
- 组条件:having是筛选分组的条件。不满足条件的分组,其中的记录不会被获取。
- 排序:asc为升序,desc为降序;其中asc为默认值,可省略。
以下是一个例子:
--从s_emp查询id、first_name和last_name,
--将first_name、last_name和title拼接起来并使用空格和逗号隔开
select id, first_name||' '||last_name||','||title as name
from s_emp
where id > 5 and name like 'A%' -- 其中id需大于5且name以A开头
order by id; -- 以id升序排序
函数
单行函数
字符函数
ascii(‘x’) | 获取ASCII码 | 120 |
---|---|---|
concat(‘Hello’, ‘World’) | 连接两个字符串 | HelloWorld |
instr(‘Hello’, ‘o’) instr(‘Hello’, ‘l’) instr(‘Hello’, ‘l’, -1) | 从指定位置查找字符 默认从头开始查找 找到返回下标 | 5 3 4 |
length(‘Hello’) | 字符串长度 | 5 |
lower(‘Hello’) | 小写转换 | hello |
upper(‘Hello’) | 大写转换 | HELLO |
initcap(‘hello’) | 首字母大写,其余小写 | Hello |
ltrim(‘Hello’, ‘He’) | 从左边第一个字符开始删去,默认删空格 | llo |
rtrim(‘Hello’, ‘lo’) | 从右边第一个字符开始删去,默认删空格 | Hel |
trim(’=Hello=’, ‘=’) | 从两边第一个字符开始删去,默认删空格 | Hello |
replace(‘Hello’, ‘l’, ‘i’) | 在字符串中查找并替换 | Heiio |
substr(‘Hello’, 1, 2) substr(‘Hello’, 1) | 从指定位置截取几个字符 默认截取到末尾 | He Hello |
数值函数
abs(x) | 求绝对值 |
---|---|
acos(x) | 求反余弦 |
cos(x) | 求余弦 |
sin(x) | 求正弦 |
asin(x) | 求反正弦 |
log(x,y) | 求x为底y的对数 |
mod(x,y) | 求余数,当除数为0时为原数据 |
power(x,y) | x的y次幂 |
sqrt(x) | 求平方根 |
ceil(x) | 向上取整 |
floor(x) | 向下取整 |
round(x[,y]) | x在小数点后第y位四舍五入(y为负数则为小数点前,默认为0) |
trunc(x[,y]) | x在第y位截断,y默认为0 |
日期函数
除了sysdate,其它日期都需要使用’包裹。日期默认格式如02-9月 -20(中文)或02-SEP-20(英文)。
sysdate | 获取当前时间,进行加减操作时单位是天 |
---|---|
months_between(t1, t2) | 两个日期之间相差的月份数 |
add_months(t, x) | 往后推x月的日期,x是数值 |
next_day(t, ‘weekday’) | 下一个星期几的日期 |
last_day(t) | 获取这个日期所在月份的最后一天 |
round(t, ‘格式字符串’) | 对日期进行四舍五入 |
trunc(t, ‘格式字符串’) | 对日期进行向下截断 |
转换函数
- nvl(A, b):将A列的空值显示为b
- to_char(x, ‘格式字符串’):将日期或数值x转为字符格式。格式串范例:$9,999.00、L9,999.00、999PR、rr-mm-dd等。具体格式见下表:
9.9 指定位置返回小数点 99,99 指定位置返回一个逗号
$999 数字开头显示美元符号 L999 数字开头显示本地的货币符号
999PR 如果数字是负数则用尖括号表示(如-3变为<3>)
yyyy 四位数的年份 rrrr 四位数的年份(5050格式)
yy 两位数的年份 rr 两位数的年份(5050格式) mm 两位数的月份(数字)
d 一周的星期几 dd 一月的第几天 ddd 一年的第几天
year 英文的年份 month 英文全称的月份 mon 英文简写的月份
ddsp 英文的当月第几天 ddspth 英文序列数的当月第几天
day 全英文的星期 dy 简写的英文星期
hh 时(12小时制) hh24 时(24小时制) mi 分 ss 秒
am 十二小时制的上午或下午
- to_number(‘字符’):将字符转换为数值。不可转换会报错。
- to_date(‘字符’, ‘格式字符串’):将字符转换为日期。格式字符串见上方。
聚合函数
聚合函数又称多行函数、分组函数、组函数,只出现在having、select和order by之后。聚合函数包括:
avg | sum | max | min | count |
---|---|---|---|---|
平均值 | 求和 | 最大值 | 最小值 | 非空记录个数 |
这些函数会对分组(group by)的参数列数据进行聚合,得到一个值。
哑表和伪列
- 哑表:一个一行一列的表,由Oracle内部自动创建,表名为Dual。Dual表主要用于操作系统变量或求一个表达式的值,这些操作都不需要一个表的参与,但为了语法需要因此使用Dual表来使语句成立。
- 伪列:rownum是Oracle中每个表都具有的一个特殊的列。它用于标识一个表中每一行的行数。它的大小从1开始,直到这个表的末尾。使用rownum当做条件查询时,只能等于1、大于0或小于任何数(且在where之后),否则没有任何查询结果。可以通过子查询,使用rownum完成分页查询。
多表查询
- 笛卡尔积:又称直积。对所有表的每条记录都进行连接。行数为所有表记录的乘积。
select *
from s_emp, s_dept;
- 连接查询:按条件对两张(或以上)表的记录进行连接。即对笛卡尔积按条件进行筛选。
- 等值连接:对不同表之某两(几)列值的相等记录进行连接,如果某一列的值在另一张表的那一列不存在,则数据不会显示出来。
select *
from s_emp e, s_dept d
where e.dept_id = d.id; -- 不满足该等值条件的dept_id和id将不会显示出来
- 外连接:在进行等值连接的同时,额外展示某张表的数据。外连接需要使用on替代where。
- 左外连接
- 右外连接
- 全连接:又称全外连接。额外展示两张表的数据。
from t1 [left|right|full] [outer] join t2
on t1.id = t2.id;
-- 简化写法(仅Oracle)
from t1, t2
where t1 = t2(+) -- 左外连接
where t1(+) = t2 -- 右外连接
-- 全连接没有简化写法
- 自连接:同一张表的不同列进行连接。需要对表起别名。
select *
from s_emp e1, s_emp e2
where e1.manager_id = e2.id;
- 不等值连接:按不等条件对两张表的记录进行连接。
- 结果集运算:使用SQL语句查询出的结果被称为结果集。可以使用以下运算符号对两个查询进行连接,获取一个集合运算结果。
- 并集:union
- 全并集:union all,不会去除重复的记录
- 交集:intersect
- 差集:minus,常用于分页查询,但开销较大
select last_name, dept_id, name
from s_emp, s_dept
where s_emp.dept_id = s_dept.id(+)
union
select last_name, dept_id, name
from s_emp, s_dept
where s_emp.dept_id(+) = s_dept.id;
子查询
子查询是将一个查询的查询结果用于另一个查询。子查询的查询结果可以是一个聚合函数的值,也可以是一张表。故它可以充当from中的一张表,也可以是where中的一个值。
-- 查询平均工资比41号部门的平均工资高的部门中员工的信息
-- 3.根据编号获取这些部门的平均工资
select last_name, salary, dept_id
from s_emp
where dept_id in(
-- 2.查询比41号部门的平均工资高的部门的编号
select dept_id
from s_emp
group by dept_id
having avg(salary) > (
-- 1.查询41号部门的平均工资
select avg(salary)
from s_emp
where dept_id=41
)
);
DML、TCL
DML即数据操纵语言,用于修改数据库中的数据。TCL即事务控制语言,主要包含事务控制的内容。只有DML语句才会产生事务。
操作数据(DML)
DML和DQL一起组成了数据库的“增删改查”(Create-Read-Update-Delete)四大功能。所有的DML语句都需要结合TCL语句才能够生效(即存储到磁盘中)。
-- 插入数据
insert into [表名(列1,列2,..)] [values(值1,值2,...) | DQL语句];
insert into t_user(id, name, email, gender, age, birthday)
values(2,'tom','abc2@qq.com','f',20,'11-8月-98');
insert into t_user(id,name,birthday)
select id,last_name,start_date
from s_emp;
-- 修改数据
update 表名 set 列1=值1,列2=值2,.... [where 条件];
update t_user set name = 'zhangsan' where id = 18;
-- 删除数据
delete from 表名 [where 条件];
delete from t_user where name = 'zhangsan';
- 插入时,若列名省略不写,则按列的顺序进行插入。若该列数据可以为空,则该列数据可以省略不写。也可以将查询结果作为数据插入到表中。注意关键字是values。
- 修改和删除时,如果不附加where字句,就会修改或删除全部数据。
- 当某列拥有外键时,该列插入和修改的取值就需要是参照列中的值(也有可能是null值)。若删除了被参照的数据,声明外键时使用的on delete就会发挥作用,指导引用了该数据的记录应该怎样操作。例如,当声明为on delete cascade时,删除参照的数据后,引用了该数据的记录也会被级联删除。
事务控制(TCL)
事务(Transaction)是由一系列对数据库表中数据进行更新操作所组成的一个执行逻辑单元。只有DML语句才会产生事务。只有当前不存在事务时,才会新建一个事务,否则使用之前的事务。
事务相当于一个缓存,所有对数据的操作都记录在缓存中,直到遇到写入或撤销操作才会清除缓存。
只有在执行commit、rollback、DDL语句时才能可以结束当前事务。
- commit能够将事务造成的影响进行提交,从而结束事务。DDL语句在执行前后也会提交事务。
- rollback能够将事务进行回滚了,默认回滚到事务开始的状态。可以使用savepoint关键字记录某一个事务状态~~,让回滚不滚那么久远~~。
insert into t_user values(1,'tom',1000);
savepoint A;
insert into t_user(id,name) values(2,'zs');
savepoint B;
delete from t_user;
rollback to B; -- 回滚到B记录点
commit; -- 提交事务
事务具有4个特征(ACID):
- 原子性(Atomicity):事务不可分割,一个事务的所有DML操作要么同时成功,否则同时失败。
- 一致性(Consistency):事务执行的结果必须使数据库从一个一致性状态变到另一个一致性状态。
- 隔离性(Isolation):事务操作应该相互独立。
- 持久性(Durability):事务的影响在结束之后能够持久存在。
可以看出,隔离性能够保证原子性,原子性能够保证一致性。
在数据库的并发访问中,可能会出现以下问题:
- 脏读(未提交读):能够读到未提交的数据。即还没有执行commit,就读到了修改过后的数据。
- 不可重复读:一次事务中重复读某一个数据不一致。造成的原因主要是在两次读之间,其它事务对该数据进行了修改。
以上两个针对的是更新(update)操作。
- 幻读:一次事务中按相同条件筛选的数据记录不一致。造成的原因主要是在两次筛选之间,其它事务对该表进行了修改。
以上主要针对插入(insert)和删除(delete)操作。
数据库的隔离级别能够解决以上三个问题:
read-uncommitted | read-committed | repeatable-read | serializable |
---|---|---|---|
最低级别 | 提交之后才能读,解决脏读 | 解决脏读和不可重复读 | 三个问题都解决 |
- 级别越高,解决的问题越多,但效率越低。
- 并不是所有数据库都支持这4种事务隔离级别。Oracle只支持第2种和第4种,而MySQL四种都支持。不同的数据库的版本支持的情况也不一样。
Set Transaction Isolation Level Read Uncommitted
Set Transaction Isolation Level Read Committed -- Oracle默认级别
Set Transaction Isolation Level Read Repeatable
Set Transaction Isolation Level Serializable