数据库
一、常用数据库
分类 | 产品 | 特点 |
---|---|---|
小型 | access、foxbase | 负载量小,用户大概 100人以内 (留言板、信息管理 系统) ;成本在千元之内 ,对安全性要求不高 |
中型 | sqlservler、mysql | 负载量,日访问在 5000~10000 ;成本在万元以内 (商务网站) ;满足日常安全需求 |
大型 | sybase、db2、oracle | 海量负载,可以处理海量数据( sybase<oracle<db2 海量处理能力) ;安全性高,相对 |
二、SQL语言
命令分类:
分类 | 命令 |
---|---|
DDL | create:创建;drop:删除;alter:修改;rename:重命名;truncate:截断 |
DML | insert:插入;delete:删除;update:更新;select:查询 |
DCL | grant:授权;revoke:回收权利;commit:提交事务;rollback:回滚事务 |
三、数据库查询语句
3.1 查询列
(1)去重
使用distinct去重,确保结果的唯一性
select distinct deptno from emp;
(2)取别名
select ename as "雇员 姓名" from emp;
select ename "雇员姓名" from emp;
select ename 雇员姓名 from emp;
select ename as 雇员姓名 from emp;
select ename as " Ename" from emp;
(3)字符串
‘ ’(单引号):表示字符串
“ ”(双引号):表示按照双引号中的内容原样输出
||表示拼接字符串
select ename||'a'||'-->' info from emp;
--双引号示例
select ename 名字 from emp; --可行
select ename 名 字 from emp; --不可行,中间有空格
select ename "名 字" from emp; --可行,该列以名 字 原样输出
(4)伪列
不存在的列,构建虚拟的列
select empno, 1*2 as count,'cmj' as name,deptno from emp;
(5)虚表
用于计算表达式,显示单挑记录的值
select 1+1 from dual;
(6)null与nvl函数
null遇到数字参与运算的结果是null,遇到字符串为字符串
select 1+null from dual; --结果为空 null
select '1'||null from dual; --结果为 1
select 1||'2'||to_char(null) from dual; --结果为 12
select ename,sal*12+comm from emp;
--nvl 内置函数,判断comm是否为 null, 如果为空,取默认值 comm1 ,否则取comm
select ename,sal*12+nvl(comm,comm1) from emp;
3.2 查询行
(1)比较条件
= 、>、 <、 >=、 <=、 != 不等、 <> 不等
(2)且或非
and、 or、 not
(3)null与is
null不能使用条件判断,只能使用is
--存在佣金的员工名称
select * from emp where comm is null;
--不存在佣金的员工名称
select * from emp where comm is not null; --不能为 not is null
select * from emp where not comm is null;
(4)集合操作
Union、Union All、Intersect、Minus
Union,并集(去重) 对两个结果集进行并集操作,不包括重复行同时进行默认规则的排序;
Union All,全集(不去重) 对两个结果集进行并集操作,包括重复行,不进行排序 ;
Intersect,交集(找出重复) 对两个结果集进行交集操作,不包括重复行,同时进行默认规 则的排序;
Minus,差集(减去重复) 对两个结果集进行差操作,不包括重复行,同时进行默认规则的排 序
--查询工资大于 1500 或含有佣金的人员姓名
--union 去除重复行
select ename from emp where sal>1500 union
select ename from emp where comm is not null;
-- union all 不去除重复行
select ename from emp where sal>1500
union all
select ename from emp where comm is not null;
--查询显示不存在雇员的所有部门号。
select deptno from dept
minus
select distinct deptno from emp
--查询工资大于 1500 且含有佣金的人员姓名
select ename,sal,comm from emp where sal>1500
intersect
select ename,sal,comm from emp where comm is not null;
(5)模糊查询
模糊查询,使用通配符:
- %:零个及以上(任意个数的)的字符
- _:一个字符
- 遇到内容中包含 % _ 使用escape(‘单个字符’)指定转义符
--查询员工姓名中包含字符 A 的员工信息
select * from emp where ename like '%A%';
--查询员工姓名中包含第二个 A 的员工名称信息
select * from emp where ename like '_A%';
--数据中 员工姓名中 存在 _ % , 如何查找:
--1) 、编写测试数据
insert into emp(empno,ename,sal) values(1000,'t_%test',8989);
insert into emp(empno,ename,sal) values(1200,'t_tes%t',8000);
--2) 、查找
--escape 括号中的字符表示将表达式的该字符作为转义字符,转义该字符后面的字符
--查询员工姓名中包含字符 % 的员工名称 岗位 工资 部门编号
select ename,job,sal,deptno from emp where ename like '%a%%' escape('a'); --将a作为转义字符
--查询员工姓名中包含第二个 _ 的员工名称 岗位 工资 部门编号
select ename,job,sal,deptno from emp where ename like '%a_%' escape('a'); --将a作为转义字符
select * from t11 where name like '%\_%' escape '\';
(6)in与exists
in相当于使用or的多个等值,定值集合 ,如果存在子查询,确保类型相同、字段数为1,如果记录多,效率不高,用于 一些 少量定值判断上:
select * from emp where sal in(900,800)
--子查询 ( 查询中再有查询 ) in 只能存在一个字段
select * from emp where sal in (select sal from emp e where deptno=10)
--10 或 30 部门的雇员信息
select * from emp where deptno in(10,30);
--部门名称为 SALES 或 ACCOUNTING 的雇员信息
select deptno from dept where dname in('SALES','ACCOUNTING');
select *
from emp
where deptno in
(select deptno from dept where dname in ('SALES', 'ACCOUNTING'));
exists条件为true,存在记录则返回结果,后续不再继续比较查询,与查询的字段无关,与记录有关:
select *
from emp
where exists
(select deptno,dname from dept where dname in ('SALES',
'ACCOUNTING'));
3.3 排序
使用 ORDER BY 排序,排序不是真实改变存储结构的顺序,而是获取的集合的顺序。
-
顺序:asc(升序 ,默认) desc(降序)
-
多字段:在前面字段相等时,使用后面的字段排序
-
空排序:降序为desc,注意null为最前,升序为asc,注意null为最后
注意:null默认值为正无穷,改变null的位置有两种方式:
- null放在排序的最前面:在排序后面直接加nulls first;与排序方式无关
- null放在排序的最后面:在排序后面直接加nulls last;与排序的方式无关
-- 按工资降序
select * from emp order by sal desc;
--null 问题
select * from emp order by nvl(comm,0),comm desc;
select * from emp order by comm nulls first;
--奖金排序,null放前面
select * from emp order by comm asc nulls first;
--奖金排序,null放后面
select * from emp order by comm asc nulls last;
四、单行函数
4.1 日期函数
- sysdate/current_data 以date类型返回当前日期
- add_month(d,x) 返回加上X月后的日期d的值
- last_day(d)返回所在月份的最后一天
- months_between(date1,date2)返回date1和date2之间月的数目
- next_day(sysdate,‘星期一’) 下一次出现星期一的日期
(1)当前日期
select sysdate from dual;
select current_date from dual;
(2)修改日期(加减日期)
-- 2天以后是几号
select sysdate+2 from dual;
-- 所有员工入职的3天后是几号
select hiredate 入职日期,hiredate+3 入职三天后 from emp;
(3)修改月份
-- 查询所有员工的试用期期到期(转正的日期) 3个月试用期
select hiredate,add_months(hiredate,3) from emp;
(4)月份之差
-- 查询所有员工到目前为止一共工作了几个月
select hiredate,months_between(sysdate,hiredate) from emp;
(5)最后一天
-- 查询当前月的最后一天
select last_day(sysdate) from dual;
(6)下一次出现的时间
-- 下一次星期三是几号
select next_day(sysdate,'星期三') from dual;
4.2 日期转换函数
- to_date(c,m) :字符串以指定格式转换为日期,c表示字符串,m表示日期格式
- to_char(d,m) :日期以指定格式转换为字符串
-- 设定一个特定的时间(用一个特定的时间字符串转换为日期)
--to_date()
select to_date('2020/7/22 11:30:34','yyyy/mm/dd hh24:mi:ss')+1 from dual;
--to_char()
select to_char(sysdate,'yyyy-mm-dd hh12:mi:ss') from dual;
select to_char(sysdate,'yyyy"年"mm"月"dd"日" hh12:mi:ss') from dual;
4.3 其它函数(decode(),(case.when.then.else.end))
-- decode 判断decode(判定字段,校验字段值1,结果1,校验字段2,结果2。。默认值)
--给每个部门后后面添加一个伪列,如果10部门,伪列显示为十,二十,三十...
select deptno,dname,decode(deptno,10,'十',20,'二十',30,'三十','四十') from dept;
select deptno,
dname,
(case deptno
when 10 then
'十'
when 20 then
'二十'
when 30 then
'三十'
else
'四十'
end)
from dept;
五、组函数
- count:统计记录数
- max、min:最大值 最小值
- sum:求和
- avg:平均值
注意:
- 组函数不能与单条记录同时查询
- 组函数不能用在where中,能使用的地方有select或者having
- null不参与运算
5.1 count
-- 统计一下一共有多少个员工
select count(empno) from emp;
select count(deptno) from emp;
select count(1) from emp;
select count(*) from emp;
select deptno from emp;
-- 统计一共有几个部门
select count(1) from dept;
-- 统计有员工存在的部门总数
select count(distinct deptno) from emp;
-- 统计20部门一共有多少人
select count(1) from emp where deptno=20;
5.2 max min
-- 查询本公司的最高工资和最低工资
select max(sal),min(sal) from emp;
--查看30部门的最高工资和最低工资
select max(sal),min(sal) from emp where deptno=30;
--查询 最高薪水的员工姓名, 及薪水
select ename,sal from emp where sal=(select max(sal) from emp);
5.3 sum
-- 计算本公司每个月一共要在工资上花费多少钱
select sum(sal) from emp;
-- 计算20部门每个月的工资花销
select sum(sal) from emp where deptno=20;
5.4 avg
-- 请查询出 20部门的平均工资
select avg(sal) from emp where deptno=20;
-- 查询工资低于平均工资的员工编号,姓名及工资
select empno,ename,sal from emp where sal<(select avg(sal) from emp);
--查看 高于本部门平均薪水员工姓名
select ename from emp e where sal>(select avg(sal) from emp e2 where e.deptno=e2.deptno);
六、分组(重)
- 分组:group by
- 过滤组:having
- 语法:select 内容 from 数据源 where 行过滤条件 group by 分组字段1,分组字段2… having 组过滤条件 order by 排序字段;
- 执行流程:from–> where --> group by --> having --> select --> order by
注意:select后面跟的查询的内容要存在group by 中的分组字段中的内容
-- 统计每个部门的员工数,和部门编号,按照员工个数升序排序
select count(ename),deptno from emp group by deptno order by count(1);
-- 查询每个工种的最高工资以及工种
select job,max(sal) from emp group by job ;
-- 查询平均工资在1500到2000之间的部门平均工资和部门编号
select deptno,avg(sal) from emp group by deptno having avg(sal) between 1500 and 2000;
-- 查询工资高于20部门平均工资的员工
select ename from emp where sal>(select avg(sal) from emp);
七、92表连接
7.1 笛卡尔积(对乘)
select * from emp,dept;
7.2 等值连接(在笛卡尔积基础上 取条件列相同的值)
--查询所有员工的信息以及所在的部门信息
select * from emp,dept where emp.deptno=dept.deptno;
7.3 非等值连接(> < != <>between and)
--查询每一个员工的信息以及薪资等级信息
select * from emp e,salgrade s where e.sal between s.losal and s.hisal;
--查询员工信息以及所在部门信息以及薪资等级信息
select *
from emp e, dept d, salgrade s
where e.deptno = d.deptno
and e.sal between s.losal and s.hisal;
7.4 自连接(来自于同一张表)
--查询由上级的员工信息以及这个员工的上级经理人信息
--员工表emp =--> 员工表 经理人表
select * from emp e1,emp e2 where e1.mgr=e2.empno;
7.5 外连接
--内连接:满足条件的才展示
select * from emp,dept where emp.deptno=dept.deptno;
--外连接: 有一些不满足连接条件的数据可以是它展示
--主表: 主表中的数据无论是否满足连接条件都能展示
--左外连接 : from 后 主表在逗号左边就是左连接
--右连接 :from 后 主表在逗号右边就是右连接
--查询所有的员工信息以及这个员工的上级经理人信息
--员工表作为主表,无论这个员工是否存在上级,都要显示
--设置主表: 在where后的连接条件,主表的对面位置(+)
select * from emp e1,emp e2 where e1.mgr=e2.empno(+);
八、99连接
8.1 交叉连接
--笛卡尔积 cross join
select * from emp, dept;
select * from emp cross join dept;
8.2 自然连接
注意: 同名字段不能指明出处
--自然连接 natural join: 自动把同名列|主外键列 做等值连接
select deptno, dname, ename from emp natural join dept;
8.3 join using连接
--join .. using(同名字段) 使用指定的同名字段做等值连接
select deptno, dname, ename from emp join dept using (deptno);
8.4 join on连接
--join .. on 等值|非等值连接 因为连接条件由你自己指定 ---先连接过滤
select e.deptno, dname, ename
from emp e
join dept d
on e.deptno = d.deptno
where e.deptno = 30;
--查询员工以及员工的薪资等级信息
select * from emp e join salgrade s on e.sal between s.losal and s.hisal;
8.5 外连接
--左外连接 left join : 主表在左边
--右外连接 right join : 主表在右边
select * from emp e1 left join emp e2 on e1.mgr=e2.empno;
select * from emp e1 right join emp e2 on e1.mgr=e2.empno;
8.6 全连接
--全连接 : full join 两张表都作为主表
select * from emp e1 full join emp e2 on e1.mgr=e2.mgr;
8.7 rowid
rowid :是一个伪列,相当于对象的地址,每一行记录的唯一标识,插入表中时候确定,其实不是地址
作用:可以为表中的数据做去重,可以根据每条数据的rowid进行区分,如果表有主键,可以根据主键或者唯一字段去重
--需求: 表中多条重复数据只保留一条,其余删掉
--找到那些要保留的数据
select min(rowid) from tb_student group by id,name,course,score;
--找到那些要删除的数据
select rowid
from tb_student
where not
rowid in
(select min(rowid) from tb_student group by id);
8.8 rownum
rownum :是一个伪列,从1开始,每次+1,先确定结果集之后再加
作用: 用来做分页,保证数据根据指定规则排序之后,rownum也要保证规律,从1开始,每次+1
注意:根据不同的字段排序,排序和rownum顺序可能不同,如果rownum没有规律,就在外面嵌套一个select,外部select的rownum就是有规律的
--查询员工信息, 根据薪资进行排序,降序排序, 每页显示num = 3条数据,显示第i = 2页数据
--嵌套的select为了让rownum有顺序有规律,先排序再确定rownum
select empno, ename, sal, rownum n from emp order by sal desc --先出结果集然后rownum++ 然后排序 因此最后出来的结果的rownum是无序的
select empno, ename, sal, rownum num from (select empno, ename, sal, rownum n from emp order by sal desc);
--又嵌套的select目的: 为了在where判断之前就确定rownum,因为执行流程,如果当前这次select的结果集还没确定,没办法做判断 先走from--where--select
select *
from (select empno, ename, sal, rownum num
from (select empno, ename, sal, rownum n from emp order by sal desc))
where num >= 4
and num <= 6;
九、视图和索引
9.1 视图
- 视图:介于表与结果集之间的虚拟表
- 普通视图:结果集中的数据没有真实存储,当原表被删除了,普通视图也会变得无效,普通视图被修改了,原表中数据也会被修改
- 物化视图:当原表被删除了,物化视图任然有效
--create or replace view 视图名称 as 结果集;
create or replace view vw_emp_haha as select empno,ename,sal from emp where deptno in (10,30);
create or replace view vw_student as select * from tb_student;
/*
*授权: grant dba to scott; 回收: revoke dba from scott;
*/
grant dba to SCOTT;
--从视图中查询数据
select * from tb_student;
select * from vw_student;
--删除视图
drop view vw_student;
9.2 索引(sql优化|调优)
- 作用:加快查询效率, 大量的根据表中的某个字段做查询时候,可以为这个字段添加索引
--create index 索引名 on表名 (字段列表...) drop index 索引名
create index index_emp_sal on emp(sal);
drop index index_emp_sal;
十、创建表
- 主键约束:primary key
- 非空约束:not null
- 检查约束:check()
- 唯一约束:unique
- 默认约束:default(默认值)
- 外键约束: foreign key
10.1 不添加约束
--创建表 (不添加约束)
create table 表名(
字段名 类型,
...
字段名 类型
)
create table haha(
hahaid number(3),
hname varchar2(15)
)
10.2 创建的同时添加约束
--创建表同时添加约束(1.默认名字 2.指定名字)
create table t_student(
--主键约束
sid number(3) primary key,
--非空约束
sname varchar2(12),
--检查约束(0~150)
sage number(3) check(sage between 0 and 150),
--检查约束
sgender char(1 char) check(sgender in ('男','女')),
--唯一
sphoneNum number(11) unique not null
)
10.3 创建的同时添加有名字的约束
--创建表 的同事指定约束名字,直接添加在字段的后面,不需要指定为哪一个字段添加约束
create table t_student(
--主键约束
sid number(3) constraints pk_student_sid primary key,
--非空约束
sname varchar(12) constraints snameNotNull not null,
--检查约束
sage number(3) check(sage between 0 and 150),
--检查约束
sgender char(1 char) check(sgender in ('男','女')),
--唯一
sphoneNum number(11) unique not null
)
10.4 在表结构最后添加约束
--创建表 的同事指定约束名字 ,需要指定为哪一个字段添加约束,表结构结束之前
create table t_student(
--主键约束
sid number(3) constraints pk_student_sid primary key,
--非空约束
sname varchar(12) constraints snameNotNull not null,
--检查约束
sage number(3) check(sage between 0 and 150),
--检查约束
sgender char(1 char) check(sgender in ('男','女')),
--唯一
sphoneNum number(11) ,
constraints number_check check(sphoneNum is not null)
)
create table t_student(
sid number(3) primary key,
sname varchar2(12) not null,
--外键约束 这个学生所在的班级编号 班级表的主键
cid number(5),--references t_class(cid)
-- constraints 约束名 foreign key(字段) references t_class(cid)
constraints fk_student_cid foreign key(cid) references t_class(cid)
);
10.5 创建表之后追加约束
alter table t_student add constraints name_check check(length(sname)>=3);
10.6 删除约束
--删除约束
alter table t_student drop constraints name_check;
10.7 约束的开启和禁用
--约束开启和禁用
alter table t_student disable constraint fk_student_cid;
alter table t_student enable constraint fk_student_cid;
10.8 注释
--注释
comment on table t_class is '班级表';
comment on column t_class.cid is '班级编号,主键';
comment on column t_class.cname is '班级名称';
十一、删除表
--学生表 班级表
--班级表 -->主表
create table t_class(
cid number(5) primary key,
cname varchar2(10) not null
);
--学生表 -->从表
create table t_student(
sid number(3) primary key,
sname varchar2(12) not null,
--外键约束 这个学生所在的班级编号 班级表的主键
cid number(5),--references t_class(cid)
-- constraints 约束名 foreign key(字段) references t_class(cid)
constraints fk_student_cid foreign key(cid) references t_class(cid)
);
--插入数据
insert into t_class values(40,'java40期');
insert into t_class values(41,'java41期');
insert into t_student values(100,'郑南杰',40);
insert into t_student values(101,'张炳松',40);
insert into t_student values(102,'吴东洋',42);
11.1 主从表的关系下删除表
11.1.1删除从表
--删除从表可以直接删除
drop table t_student;
drop table t_class;
11.1.2 删除主表
--不能直接删除主表,默认要求先删除从表再删除主表
--删除主表的同时,删除主外键约束关系
drop table t_class cascade constraint;
11.2 主从表关系下删除数据
11.2.1 删除主表中的数据
--删除没有被从表引用的主表数据
delete from t_class where cid=41;
--删除被从表中数据所引用的主表数据时: 3种解决方案
--1) 默认先删除从表中引用了当前主表数据的从表数据
delete from t_class where cid=40;
--2) 删除主表数据的同时,就把从表中引用了当前主表数据的从表数据一起删掉 on delete cascade 在外键约束后面添加
--3) 删除主表数据的同时,就把从表中引用了当前主表数据的从表数据外键字段设置为null on delete set null
十二、DML语句
12.1 insert
12.1.1 插入单条数据
insert into 表(指定列) select 查询列 from 源表 where 过滤数据;
insert into 表(指定列) values(值列表);
insert into 表名 select 查询列 from 源表 where 过滤数据;
insert into 表名 values(值列表 );
create table hehe(
id number,
name varchar2(15),
age number(3),
gender char(3)
)
insert into hehe(id,name) select deptno,dname from dept where deptno=10;
insert into hehe(id,name) values(102,'lisa');
insert into hehe select id,name,age,gender from hehe where id=101;
insert into hehe values(101,'张三',18,'女');
12.1.2 插入多条数据
注意:insert into 表(指定列) select 查询列 from 源表 where 过滤数据;使用这种方式插入多条数据时,每个select之间使用union进行连接
insert into emp(empno, ename, deptno) select empno, ename, deptno from emp where empno = 7654
union
select empno, ename, deptno from emp where empno = 7566;
12.2 update
12.2.1 插入单条数据
update 表名 set(字段列表)=(select 字段列表 from 源表 where 过滤源表记录 ) where 更新记录的条件 手动更改字段值:
update 表名 set 字段=值 [,…] where 过滤行记录;
update hehe set name='lisi',age=20 where id=102;
update hehe set(name,age)=(select dname,deptno from dept where deptno=20) where id=10;
12.2.1 插入多条数据
注意:多个select之间使用“;”来间隔开,在头部加begin,在尾部加end;
begin
update hehe set name='lisi',age=20 where id=102;
update hehe set name='zhangsan',age=20 where id=104;
end;
12.3 delete
12.3.1 删除单条数据
–删除数据 一个 多个 所有
delete from 数据源 where 条件;
delete from t_class where cid=41;
12.3.2 删除多条数据
注意:使用“,”将要删除的信息的索引间隔开
delete from t_class where cid in (40,30);
12.3.4 delete与truncate区别
删除表中的所有数据:
- delete删除的时候会默认开启事务,如果删除的数据被引用了,可以用上面所讲述的三种解决方案进行操作
- truncate删除的时候不会开启事务,从表结构上进行检查,是否被引用,存在被引用就不能进行数据的截断
delete from t_class;
truncate table t_class;
十三、序列
作用:帮助管理字段值(数select * from t_class;值, 有规律,可寻的),可以用来自动生成简单的有规律的主键的值(mysql中还可以使用auto_increment的方式使得整数类型的主键自增)
注意:获取序列的值: currval 当前值 nextval下一个值 第一次使用要nextval获取最新值
--create sequence 序列名 start with 起始值 increment by 步进;
create sequence sq_class_id start with 10 increment by 1;
--使用sq_class_id.nextval作为主键值
insert into t_class values(sq_class_id.nextval,'java');
十四、事务
14.1 事务的四大特性
原子性(Atomic):事务中所有数据的修改,要么全部执行,要么全部不执行 |
---|
一致性(Consistence):事务完成时,要使所有所有的数据都保持一致的状态 |
隔离性(Isolation):事务应该在另一个事务对数据的修改前或者修改后进行访问 |
持久性(Durability):保证事务对数据库的修改是持久有效的,即使发生系统故障,也不应该丢失 |
14.2 事务的并发问题
脏读(Dirty read)
当一个事务正在访问数据并且对数据进行了修改**,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据**,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
不可重复读
(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
不可重复读:在同一事务中,两次读取同一数据,得到内容不同
事务1:查询一条记录
-------------->事务2:更新事务1查询的记录
-------------->事务2:调用commit进行提交
事务1:再次查询上次的记录
此时事务1对同一数据查询了两次,可得到的内容不同,称为不可重复读
幻读
(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同
事务1:查询表中所有记录
-------------->事务2:插入一条记录
-------------->事务2:调用commit进行提交
事务1:再次查询表中所有记录
此时事务1两次查询到的记录是不一样的,称为幻读
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读
14.3 事务的隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。
事务的隔离级别从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | √ | √ | √ |
READ COMMITTED | × | √ | √ |
REPEATABLE READ | × | × | √ |
SERIALIZABLE | × | × | × |
A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读
14.3 事务的隔离级别
事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。
事务的隔离级别从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | √ | √ | √ |
READ COMMITTED | × | √ | √ |
REPEATABLE READ | × | × | √ |
SERIALIZABLE | × | × | × |
十五、数据库设计的三大范式
注意:三大范式只是一个规范而已,可以视情况决定遵守与否。
15.1 第一范式
数据库表每一列都是不可分割基本数据项,同一列中不能有多个值。
15.2 第二范式
要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关
15.3 第三范式
确保数据表中的每一列数据都和主键直接相关,而不能间接相关