数据库索引
索引是对数据库表一列或者多列进行排序的一种结构,底层结构一般是 B+ 树,索引的主要目的是为了加快数据检索速度,当然不同的索引类型有不同的特性
索引类型
-
普通索引:由 key/index 定义,主要是为了加快检索速度,没有限制
-
唯一索引:值必须是唯一的,可以保证数据记录中每一行数据的唯一性
-
主键索引:是一种特殊的唯一索引,由
PRIMARY KEY
创建,一个表只能有一个主键,且不为空 -
组合索引:可以覆盖多个列,例如 INDEX(columnA, columnB)索引
如何建立索引
#例:为学生课程库中的 student 表建立索引
create unique index stusno on student(sno);
索引使用场景
- 大数据量的表建立索引比较有意义
- 一般频繁作为查询条件的字段会创建索引;
- 在外键建立索引,可以加快表的连接速度
- 在经常需要排序的列,建立索引,因为索引可以自动排序
- 经常使用在 where 子句中的字段加索引可以提高条件判断的速度,像那些很少或从不引用的字段,比如性别,就不要建立索引了
索引的缺陷
索引占据存储空间,另外在插入和删除时需要费时,因为索引也会动态变化,这样就降低数据维护效率
以 % 符号开头的 like 语句,索引是失效,比如 like %123%
如果数据类型出现隐式转化,也无法使用索引(如varchar不加单引号的话可能会自动转换为int型)
参考文章:https://blog.csdn.net/kennyrose/article/details/7532032
数据库事务
对系统中数据进行访问与更新的一系列操作 ,而这些操作所组成的一个程序执行逻辑单元,要么成功要么失败,一共有四大特性
原子性:事务的操作要么成功,要么失败,成功后应用到数据库,失败后对数据库没有任何影响
一致性:事务开始前和结束后,没有破坏数据的完整性约束,比如,我给你转1000万,你收到 1000 万,我肯定会减少 1000 万,如果我没有减少,或者你没有收到,这个时候是不一致的
隔离性:主要针对多个用户并发操作数据库的时候,比如我给银行卡转账的同时,不可以取钱,同一时间,只允许一个事物请求同一数据
持久性:一旦提交后,数据库中的数据就是永久的
如果不考虑事务隔离,会造成
脏读:一个事务读取了另一个事务未提交的数据
不可重复读:主要针对更新操作,在一个事务中,两次查询结果不一致
幻读:主要针对插入删除操作,在一个事务中,两次查询结果不一致
数据库通过事务隔离级别来解决读的问题
读未提交:所有的事务可以看见其它未提交事务的执行结果,很少在实际中应用,读取到未提交的数据称为脏读.
读已提交:读取已经提交的数据,可避免脏读,这也是大多数数据库系统的默认隔离级别
可重复读:MySQL的默认隔离级别,写数据锁住了满足条件的行,避免了脏读和不可重复读带来的影响,但是可能会出现幻读的现象
可串行化:隔离的最高级别,在读的时候锁住了整张表,可避免幻读的问题,但是会造成超时现象和锁竞争
那数据库为什么不默认设置为可串行化?
数据库隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大
嵌套事务
嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫 save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行
如果子事务回滚,父事务会回滚到进入子事务前建立的 save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。
如果父事务回滚,子事务也会跟着回滚,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分
事务的提交,是什么情况?
子事务先提交,父事务再提交,子事务是父事务的一部分,由父事务统一提交。
数据库三大范式
MySQL 引擎
MySQL 中的数据用各种不同的技术存储在文件(或者内存)中,不同的引擎对应不同的技术,MySQL 主要使用三种存储引擎
- InnoDB
- MyISAM
- MEMORY
首先说说 InnoDB 和 MyISAM 的区别?
InnoDB 支持事务特性,可以进行回滚,而 MyISAM 不行,MyISAM 比较适合以查询和插入为主,而 InnoDB 使用场景是那些频繁修改,安全性比较高的应用,所以 InnoDB 也是 MySQL 的默认存储引擎,但是 MyISAM 性能比较高,占用存储空间少,所以如何使用视情况而定
对于 MEMORY 存储引擎,它使用存储在内存中的内容来创建表,而且数据全部放在内存中,这样有利于数据的快速处理,提高整个表的效率
需要注意,服务器需要有足够的内存来维持 MEMORY 存储引擎的表的使用。如果内存出现异常就会影响数据,比如重启或者关机,所有数据都会消失。因此,基于 MEMORY 的表的生命周期很短,一般是一次性的
MySQL 的 MVCC 机制
是一种并发控制的方法,在数据库管理系统中实现对数据库的并发访问
乐观锁的就是基于 MVCC 实现的,乐观锁顾名思义就是很乐观,每次读写事务的时候不会上锁,因为它认为别人不会修改,但是在提交的时候会看一看数据的版本,如果版本和之前一致,就说明在这期间确实没有上锁,那就可以放心提交了
而悲观锁就是每次读写事务的时候都会上锁,在数据处理的过程中,处于锁定的状态,这样一来就会有很大的开销,但是也不能说乐观锁比悲观锁强,主要还是看具体使用场景
像乐观锁这种适用于读事务多的情况,这样可以省去锁的开销,加大系统的吞吐量,对于经常更新写的事务,加锁还是比较划算一点
以下是数据库 MySQL 的基本操作
-
数据定义:基本表、索引。
-
数据查询:简单查询、连接查询、嵌套查询。
-
数据更新(增、删、改)。
-
视图:定义、查询、更新
基本表的定义:
create table student(
sno char(9) primary key,
sname char(20) unique,
sex char(2)
);
基本表的修改
# 向student 中加入’入学时间‘列,
# 数据类型是日期类型,那么他的mysql数据怎么写?
alter table student add 列名 data(类型)
# 或者想要修改某一属性的数据类型该怎么写?
alter table student alter 列名 新类型
# 删除基本表
drop table 表名
索引的建立与删除,格式一般为
create unique index <索引名> on <表名>
#例:为学生课程库中的 student 表建立索引
create unique index stusno on student(sno);
# 删除索引:drop index <索引名>
drop index stusno;
#修改索引:alter index <旧索引名> rename to <新索引名>
alter index scno rename to scc;
数据的 简单查询
查询学号和名字:select sno ,sname from student;
消除取值重复的行:意思就是如果查询出现了重复的行,那么可以使用 distinct
去重,例:select distinct sno from student
利用 where
加判断条件,例:查询年龄在20岁以下的学生姓名和年龄
select sname,age from student
where age<20;
另外还有其他比如,between on ,not between on ,in ,not in 等关键字,例:
select *from student
where age between 20 and 23;
select *from student
where age not between 20 and 23;
#下面这条语句是查询 sdept 属性在 /不在 CS,MA的学生所有信息
select *from student
where sdept in('CS','MA');
select *from student
where sdept not in('CS','MA');
字符匹配,利用谓词like
进行字符串的匹配,例:
select sname from student where sname like '马%';
select sname from student where sname like '欧阳_';
按照升序或者降序的方式进行查询
# 降序的方式
select sno,grade from student
where co='3' order by grade desc;
# 升序的方式
select sno,grade from student
where co='3' order by grade asc;
当然还有一些函数使用
# 查询学生的总数
select count(*) from student;
# 查询1号课程的平均成绩
select avg(grade) from student where cno='1';
select max(grade) from student where com='1';
数据的查询之 连接查询
连接查询针对的是多张表的联合查询
student 表
学号 sno | 姓名sname | 性别sex | 年龄age | 所在系sdept |
---|---|---|---|---|
201215121 | 李勇 | 男 | 20 | cs |
201215122 | 刘晨 | 男 | 18 | cs |
201215123 | 王敏 | 男 | 18 | ma |
201215125 | 张立 | 男 | 19 | is |
SC表
学号sno | 课程号cno | 成绩grade |
---|---|---|
201215121 | 1 | 92 |
201215121 | 2 | 85 |
201215121 | 3 | 88 |
201215122 | 2 | 90 |
201215122 | 3 | 80 |
将两张表连接,去掉重复的列属性元素,由于sno 在两张表都出现了,所以得加上前缀名,否则数据库是分不清的!
select student.sno,sname,
sex,age,sdept,cno,grade
from student,sc
where student.sno=sc.sno;
连接谓词和选择谓词组成的复合条件
查询选修2号课程且成绩在90分以上的所有学生的学号和姓名
select student.sno ,sname from student,sc
where student.sno=sc.sno
and sc.cno='2' and sc.grade>90;
嵌套查询,比如在书中提到的:查询与刘晨在同一个系的学生?
查询的时候,我们首先得知道刘晨在哪个系,然后才能查询该系的所有学生,所以查询刘晨在哪个系的语句应该嵌套在查询所有学生的里面。
select sno,sname,sdept
from student
where sdept in(
select sdept from student where sname='刘晨'
);
查询选修了课程名为信息系统
的学生学号和姓名
首先分析学生学号的姓名在Student 表,课程名存放在Course(没画出来),但是在Course 中有课程号,而课程号在CS表中也存在,所以就得通过课程号来建立两者表的连接,最后在找出学号和姓名
第一步:查出课程名信息系统 的课程号,比如课程号为3
seclect cno from course where cname='信息系统'
第二步:在SC表中找出选修了3号课程的学生学号
select sno from sc
第三步:在student 中根据第二部所查出来的学号进行查询学生姓名和学号。
select sno,sname from student
整合起来就是
# 第三步
select sno,sname from student where sno in(
# 第二步
select sno from sc where cno in(
# 第一步
select cno from course where cname='信息系统'
)
);
当然这种嵌套看起来还是挺繁琐的,我们也可以使用连接表来查询
select student.sno,sname from student,sc,course
where student.sno=sc.sno and
sc.cno=course.cno and course.cname='信息系统'
数据的更新
修改数据:例如将学生学号为 201212121 的年龄改为22岁
update student set age=22 where sno='201212121';
将所有学生年龄+1
update student set age = age+1;
将计算机科的所有学生全体成绩置位0
update sc set grade=0 where sno in (select sno from student where sdept='cs');
删除数据:例如删除学号为 201212128 的学生记录
delete from student where sno='201212128';
删除所有学生的选课记录
delete from sc;
删除计算机科学系的所有学生的选课记录
delete from sc where sno in(select sno from student where sdept='sc');
视图的定义、查询、更新
建立视图
案列1:建立信息系学生的视图
一般格式为 create view <视图名> [列名] as <子查询> [where 语句]
create view is_student #此处省略了列名
as select sno,sname,age from student
where sdept='IS'
如果要保持在进行修改和插入的操作时仍然只有信息系的学生则在where 语句后面 加:with check option
案列2:建立信息系选修了 1 号课程的学生视图
create view IS_si(sno,sname,grade)
as select student,sc where sdept='IS'
and student.sno=sc.sno and sc.cno='1';
案列3:建立信息系统选修了1号课程且成绩在90分以上的学生的视图,直接在视图1的基础上进行建立。
create view is_s2
as select sno,sname,grade from is_si
where grade>=90;
案列4:将学生的学号、平均成绩定义为视图
create view S_G(sno,Gavg)
as select sno,avg(grade) from sc
group by sno;
删除视图
格式为:drop view <视图名>
更新视图和表基本类似
总结一些视图的作用
- 简化了用户的操作
- 是用户以多种角度去看待同一数据
- 视图对重构数据库提供了一定程度的逻辑独立性
- 视图对机密数据提供安全保护
- 适当利用视图可以更清晰的表达查询
数据库的安全性
授权与收回
SQL 中采用 grant 和 revoke 语句向用户授予或收回对数据的操作权限。 一般格式为:
grant <权限> on <对象类型><对象名> to <用户>;
# 案列1:把查询 student 表的权限授予用户UI
grant select on table student to UI;
# 案例2:把堆student 表和 course 表的全部操作权限授予用户U2和U3
grant all privileges
on table student,course to U2,U3;
# 案列3:把对表SC的查询权限授予所有用户
grant select on table SC to public;
# 案列4:把查询 student 表和修改学生学号的权限授予给用户U4
grant update(sno),select no table student to U4;
# 案例5:把对表 sc 的 insert 权限授予U5用户,
# 并允许将此权限再授予其他用户
grant insert on table sc to U5 with grant option
收回权限是 revoke ,一般格式为:
revoke <权限>on <对象类型><对象名> from <用户>;
# 案例1:把用户 U4 修改学生学号的权限收回
revoke update (sno) on table student from u4;
# 案例2:收回所有用户对表 SC 的查询权限
revoke select on table sc from public;
# 案列3:把用户 U5 对 SC 表的 insert 权限收回
revoke insert on table sc from U5 cascade
# cascade 的意思就是:级联收回U6,U7的insert 权限,
# 因为 U5对sc表我的insert 权限授予过 U6和U7
数据库角色的创建
案例1:首先创建一个角色 R1
create role R1;
# 然后使用 grant 语句使角色R1 拥有 student 表的
# select ,update 、insert 权限
grant select,update,insert on table student to R1;
# 将这个角色授予王平、张明、赵玲,
# 使他们具有角色R1所包含的全部权限
grant R1 to 王平,张明,赵玲;
# 一次性收回王平的三个权限
revoke R1 from 王平;
案列2:角色的权限修改
# 使角色R1在原来的基础上增加 student 表的delete 权限
grant delete on table student to R1
#使R1 减少 select 的权限
revoke select on table student
对于视图授予权限
例:建立计算机系学生的视图,把对该视图的 select 权限授予王平,把该视图上的所有操作权限授予张明
create view CS_STUDENT as
select* from student where
sdept='CS';
grant select on CS_STUDENT to 王平;
grant all privileges on CS_STUDENT to 张明;
定义实体完整性
案例1:将student 表中的 sno 属性定义为码
# 方法一
create table student(
sno char(9) primary key,
sname char(20)not null,
sex char(2)
);
# 方法二
create table student(
sno char(9),
sname char(20) not null;
sex char(2)
primary key (sno)
);
定义参照完整性
关系模型的参照完整性在 create table 中用 foreign key 短语定义哪些为外码,用 references 短语指明这些外码参照哪些表的主码.
例:关系SC 中一个元组表示一个学生选修的某门课程成绩,(sno,cno)是主码,sno,cno 分别参照引用 student 表的主码和course 表的主码。
create table sc(
sno char(9)not null,
cno char(4) unique not null,
primary key(sno,cno),
# 在表级定义参照完整性
foreign key (sno) references student(sno),
foreign key (cno) references student(cno),
);
用户定义的完整性:属性上的约束条件,元组上的约束条件
- 列值非空 not null
- 列值唯一 unique
- 检查列值是否满足一个条件表达式 check 短语
属性上的约束条件前两个上面已近涉及到了,第三个检查列值是否满足一个条件表达式,check 短语指定列值应该满足的条件
案例1:student 表中的sex 只允许取 ’男‘、’女‘
create table student(
sno char(9) primary key,
sname char(8) not null,
sex char(2)check (sxe in('男','女')),
age smallint,
sdept char(20)
);
案列2:sc 表的 grade 的值应该在 0 - 100 之间
create table sc(
sno char(9),
cno char(4),
grade smallint check(grade>=0 and grade<=100),
primary key(sno,cno),
foreign key(sno) references student(sno)
);
元组上的约束条件定义:以check 短语定义元组上的约束条件,即元组级的限制
案列1:当学生的性别是男时,其名字不能以 Ms.打头
create table student(
sno char(9),
sname char(8) not null,
sex char(2),
age smallint,
sdept char(20),
primary key (sno),
check (sex='女' or sname not like 'Ms.%')
);