数据库,Mybatis,JDBC基础面试题

一、理论题

1 Mybatis和JDBC区别

JDBC是Java提供的一个操作数据库的API,MyBatis是对JDBC的封装。相对于JDBC,MyBatis有以下优点

  • 优化获取和释放连接
  • SQL统一管理,对数据库进行存取操作
    我们使用JDBC对数据库进行操作时,SQL查询语句分布在各个Java类中,这样可读性差,不利于维护,当我们修改Java类中的SQL语句时要重新进行编译。
    Mybatis可以把SQL语句放在配置文件中统一进行管理,以后修改配置文件,也不需要重新就行编译部署。
  • 生成动态SQL语句
  • 能够对结果集进行映射
    我们在使用JDBC进行查询时,返回一个结果集ResultSet,我们要从结果集中取出结果封装为需要的类型;在Mybatis中我们可以设置将结果直接映射为自己需要的类型,比如:JavaBean对象、一个Map、一个List等等。

2 Mybatis缓存

  • 一级缓存
    一级缓存也叫本地缓存,是在会话(SqlSession)层面进行缓存的。MyBatis 的一级缓存是默认开启的,不需要任何的配置
  • 二级缓存
    二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace 级别的,可以被多个SqlSession 共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步。如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:二级缓存 —> 一级缓存 —> 数据库

3 Mybatis接口绑定

接口绑定定义
就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法,可以有更加灵活的选择和设置。
实现方式

  • 过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定
  • 通过xml里面写SQL来绑定,指定xml映射文件里面的namespace必须为接口的全路径名

4 Mybatis中动态sql

SQL分为静态SQL和动态SQL。所谓静态SQL指的是在PL/SQL块中使用的SQL语句在编译时是明确的,执行的是确定对象。动态SQL是指在PL/SQL块编译时SQL语句是不确定的,如根据用户输入的参数的不同而执行不同的操作。编译程序对动态语句部分不进行处理,只是在程序运行时动态地创建语句、对语句进行语法分析并执行该语句。
常用标签:where,choose,when,foreach ,if,set ,sql ,include

5 Mybatis注入说明

Spring容器通过报扫描注解@MapperScan 为Mybatis的所有的接口创建代理对象.
代理对象:

  1. JDK动态代理 要求必须有接口
  2. CGLIB动态代理 有无接口都可以,但是代理对象是被代理者的子类.

6 数据库优化

  • 若对该表的查询频率比较高,则建立索引
  • 尽量使用固定长度字段和限制字段长度
  • 合理利用内存,有的数据要缓存
  • 分库分表
  • 数据库引擎
  • 预处理
  • 读写分离
    在数据库并发大的情况下,最好的做法就是进行横向扩展,增加机器,以提升抗并发能力,而且还兼有数据备份功能

7 数据库什么字段适合建索引

第一、在经常需要搜索的列上,可以加快搜索的速度;
第二、在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
第三、在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
第四、在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
第五、在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
第六、在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

8 多表查询怎么优化

1.尽量不用’*’
2.小表作驱动
3.大表建索引

9 mysql优化

  • 大量数据提交,批量新增(默认新增SQL有事务控制,导致每条都需要事务开启和事务提交;而批量处理是一次事务开启和提交,自然速度飞升)
  • 删除大量数据时,分批次删除(同时修改或删除过多数据,因为会造成cpu利用率过高,会造成锁表操作,从而影响别人对数据库的访问。)
  • group by实现分组,最好分组前过滤进行where过滤数据到最少
  • where,order by 常出现的字段创建索引
  • 多表联查,最多5张
  • 查询SQL,尽量用列名代替* (取需要的字段,节省资源,减少网络开销,而且select * 进行查询时,很可能不会用到索引,就会造成全表扫描)
  • 避免在where子句中使用or来连接条件,过滤时,尽量把范围控制到最小,尽量不要用!=或<>,like用来做模糊查询,尽量确定开始元素 where name like ‘a%’,表达式操作(容易全表扫描)
  • 用varchar代替char,因为varchar可以变长
  • 尽量使用数值替代字符串类型
  • 查询尽量避免返回大量数据
查询id为1或者薪水为3000的用户:
  反例:SELECT * FROM student WHERE id=1 OR salary=30000
  正例:# 使用union all    
       SELECT * FROM student WHERE id=1
       UNION ALL
       SELECT * FROM student WHERE salary=30000
     # 分开两条sql写
      SELECT * FROM student WHERE id=1
      SELECT * FROM student WHERE salary=30000
  理由: 使用or可能会使索引失效,从而全表扫描,对于or没有索引的salary这种情况,假设它走了id的索引,但是走到salary查询条件时,它还得全表扫描。也就是说整个过程需要三步:全表扫描+索引扫描+合并。如果它一开始就走全表扫描,直接一遍扫描就搞定。虽然mysql是有优化器的,处于效率与成本考虑,遇到or条件,索引还是可能失效的

9 MySQL数据库存储引擎

InnoDB

a、可以自动增长列,方法是:auto_increment。
b、支持事务。默认的事务隔离级别是可重复读
c、使用的锁粒度为行级锁,可以支持更高的并发。
d、支持外键约束;外键约束其实降低了表的查询速度,但是增加了表之间的耦合度。
e、配合一些热备工具可以支持在线热备份。
f、在 InnoDB 中存着缓冲管理,通过缓冲池,将索引和数据全部缓存起来,加快查询的速度;
g、对于 InnoDB 类型的表,其数据的物理组织形式是聚簇表。所有的数据按照主键来组织,数据和索引放在一块,都位于B+树的叶子节点上。

MyISAM

MyIASM是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和Innodb不同,MyIASM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。

Memory

将数据存在内存,为了提高数据的访问速度,每一个表实际上和一个磁盘文件关联。文件是frm。

  1. 支持的数据类型有限制,比如:不支持TEXT和BLOB类型,对于字符串类型的数据,只支持固定长度的行,VARCHAR会被自动存储为CHAR类型;
  2. 支持的锁粒度为表级锁。所以,在访问量比较大时,表级锁会成为MEMORY存储引擎的瓶颈
  3. 由于数据是存放在内存中,一旦服务器出现故障,数据都会丢失
  4. 查询的时候,如果有用到临时表,而且临时表中有BLOB,TEXT类型的字段,那么这个临时表就会转化为MyISAM类型的表,性能会急剧降低
  5. 默认使用hash索引
  6. 如果一个内部表很大,会转化为磁盘表。

10 mysql回表及索引覆盖

https://blog.51cto.com/u_15127491/2655676

11 哪些情况下不应该使用索引

  1. 表很小的情况下,没有必要使用索引
  2. 不经常在Where后使用的比较字段
  3. 如果表数据需要频繁修改,不建议使用索引
  4. 如果查询返回记录很多,不建议使用索引
  5. 如果where后含IS NULL /IS NOT NULL/ like ‘%输入符%’等条件,不建议使用索引。

12 什么情况下索引失效

1.如果条件中有or,即使其中有条件带索引也不会使用
注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
2.对于多列索引,不是使用的第一部分,则不会使用索引
3.like查询是以%开头
4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

13 SELECT*比SELECT字段 查询速度还快

mysql是按照数据块来存取数据的,一个数据块是8K,当你需要的数据在某个数据块上时,sql server会将整个8K的数据从磁盘上加载到内存中,而不仅仅是读取你需要的a1、a2这几个字段,从这种意义上来说,select *和select a1,a2,a3这种写法速度是一样的。

但是有一个问题在于有索引,当有索引的时候就不一样了,比如a1, a2, a3都在索引字段里面(包括inclue),这个时候如果select a1, a2,a3的话只要扫描索引就好了,而select *却要扫描表,通常这种情况下select a1, a2, a3要快一些。但是如果只有a1, a2在索引中,而a3不在索引中,那么select a1, a2, a3的时候就需要先扫描索引得到a1, a2,再通过索引找到表中对应的数据块取出来a3(注意取a3的时候是8K数据块一起取得),这时你会发现select a1, a2, a3又没什么优势了。当取得数据特别多的时候,扫描索引再去查找表,反而不如直接扫描表来的快,所以有时可能select *反而快。

所以要具体问题具体分析,具体的要看查询计划,确定问题所在。

数据最小单位是页 (也就是你所说的块)大小8K左右 另外聚集索引扫描与表扫描在IO开销与执行时间上差距非常小 可以忽略不计,索引查找与表扫描差距才是最大的,索引查找只需要选择性的找描部分数据页。

另外聚集索引数据直接存在叶子节点,而非聚集索引只是存指向数据的指针。你说的第二种情况属于书签查找,原理正如你所说!

到底是select * 快还是select a1,a2 与索引确实有系,生成的执行计划不同执行时间也会不同,当然返回所有列与返回选定列 抛开索引的影响,肯定是列越少IO的开销就越小速度也就越快

二、实战sql

1 一个叫 team 的表,里面只有一个字段name, 一共有4 条纪录,分别是a,b,c,d, 对应四个球对,现在四个球对进行比赛,用一条sql 语句显示所有可能的比赛组合

SELECT a.name,b.name from team a,team b WHERE a.name<b.name ORDER BY a.name,b.name

在这里插入图片描述

2 score表分数都>60分的学生,删除冗余数据

如下score表数据
在这里插入图片描述

CREATE TABLE `score` (
  `id` int(4) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `subject` varchar(20) DEFAULT NULL,
  `score` int(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

INSERT INTO `score` VALUES ('1', '张三', 'math', '70');
INSERT INTO `score` VALUES ('2', '张三', 'English', '89');
INSERT INTO `score` VALUES ('3', '李四', 'math', '90');
INSERT INTO `score` VALUES ('4', '李四', 'English', '45');
INSERT INTO `score` VALUES ('5', '王五', 'math', '32');
INSERT INTO `score` VALUES ('6', '王五', 'English', '32');
INSERT INTO `score` VALUES ('7', '王五', 'English', '32');

找出所有分数都>60分的学生

SELECT name FROM score GROUP BY name HAVING MIN(score)>60

删除冗余数据,比如id为6和7的就是冗余数据

DELETE from score WHERE id not in (
SELECT id FROM (
   SELECT min(id) as id  from score GROUP BY name,subject,score
  )t
)

3 学生表,课程表,分数表,教师表

已经表结构

#学生表
CREATE TABLE `student` (
  `stuId` int(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
  `stuName` varchar(30) DEFAULT NULL COMMENT '学生姓名',
  `stuAge` int(4) DEFAULT NULL COMMENT '学生年龄',
  `stuSex` varchar(4) DEFAULT NULL COMMENT '学生性别',
  PRIMARY KEY (`stuId`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
#课程表
CREATE TABLE `course` (
  `courseId` int(4) NOT NULL AUTO_INCREMENT COMMENT '课程编号',
  `courseName` varchar(30) DEFAULT NULL COMMENT '课程名字',
  `teacherId` int(11) DEFAULT NULL COMMENT '教师编号',
  PRIMARY KEY (`courseId`)
) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8;
#分数表
CREATE TABLE `scores` (
  `stuId` int(11) NOT NULL COMMENT '学生编号',
  `courseId` varchar(11) DEFAULT NULL COMMENT '课程编号',
  `score` int(11) DEFAULT NULL COMMENT '分数'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#教师表
CREATE TABLE `teacher` (
  `teacherId` int(11) NOT NULL AUTO_INCREMENT COMMENT '教师id',
  `teacherName` varchar(100) DEFAULT NULL COMMENT '教师名字',
  PRIMARY KEY (`teacherId`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

查询“100”课程比“101”课程成绩高的所有学生的学号

select a.stuId from scores a,scores b WHERE a.stuId=b.stuId and  a.courseId=100 and b.courseId=101 and a.score>b.score

查询至少选修了两门课程的的学生学号

SELECT stuId from scores GROUP BY stuId HAVING  COUNT(courseId)>=2

查询学过100并且也学过编号101课程的同学的学号、姓名

select s.stuId,s.stuName from scores a join scores b on a.stuId=b.stuId 
JOIN student s on a.stuId=s.stuId
and a.courseId=100 and b.courseId=101

查询没有学所有课的同学的学号,姓名

SELECT student.stuId,stuName from student LEFT  JOIN scores on student.stuId=scores.stuId GROUP BY stuId HAVING COUNT(courseId)< (select COUNT(1) from course)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值