MySQL数据库面试题合集

数据库索引

索引是对数据库表一列或者多列进行排序的一种结构,底层结构一般是 B+ 树,索引的主要目的是为了加快数据检索速度,当然不同的索引类型有不同的特性

索引类型

  • 普通索引:由 key/index 定义,主要是为了加快检索速度,没有限制

  • 唯一索引:值必须是唯一的,可以保证数据记录中每一行数据的唯一性

  • 主键索引:是一种特殊的唯一索引,由PRIMARY KEY创建,一个表只能有一个主键,且不为空

  • 组合索引:可以覆盖多个列,例如 INDEX(columnA, columnB)索引

如何建立索引

#例:为学生课程库中的 student 表建立索引
create unique index stusno on student(sno);

参考文章:https://www.cnblogs.com/zlingh/p/3883716.html

索引使用场景

  • 大数据量的表建立索引比较有意义
  • 一般频繁作为查询条件的字段会创建索引;
  • 在外键建立索引,可以加快表的连接速度
  • 在经常需要排序的列,建立索引,因为索引可以自动排序
  • 经常使用在 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 实现的,乐观锁顾名思义就是很乐观,每次读写事务的时候不会上锁,因为它认为别人不会修改,但是在提交的时候会看一看数据的版本,如果版本和之前一致,就说明在这期间确实没有上锁,那就可以放心提交了

而悲观锁就是每次读写事务的时候都会上锁,在数据处理的过程中,处于锁定的状态,这样一来就会有很大的开销,但是也不能说乐观锁比悲观锁强,主要还是看具体使用场景

像乐观锁这种适用于读事务多的情况,这样可以省去锁的开销,加大系统的吞吐量,对于经常更新写的事务,加锁还是比较划算一点

参考文章:https://www.cnblogs.com/moershiwei/p/9766916.html


以下是数据库 MySQL 的基本操作

  1. 数据定义:基本表、索引。

  2. 数据查询:简单查询、连接查询、嵌套查询。

  3. 数据更新(增、删、改)。

  4. 视图:定义、查询、更新

基本表的定义:

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 <视图名>
更新视图和表基本类似

总结一些视图的作用

  1. 简化了用户的操作
  2. 是用户以多种角度去看待同一数据
  3. 视图对重构数据库提供了一定程度的逻辑独立性
  4. 视图对机密数据提供安全保护
  5. 适当利用视图可以更清晰的表达查询

数据库的安全性

授权与收回
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),
);

用户定义的完整性:属性上的约束条件,元组上的约束条件

  1. 列值非空 not null
  2. 列值唯一 unique
  3. 检查列值是否满足一个条件表达式 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.%')
);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章