数据库教程:mysql 经典面试题以及优化过程

CREATE TABLE `t_dept` (`id` INT(11) NOT NULL AUTO_INCREMENT,`deptName` VARCHAR(30) DEFAULT NULL,`address` VARCHAR(40) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;CREATE TABLE `t_emp` (`id` INT(11) NOT NULL A.

CREATE TABLE `t_dept` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARCHAR(30) DEFAULT NULL, `address` VARCHAR(40) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE TABLE `t_emp` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT NULL,   `age` INT(3) DEFAULT NULL, `deptId` INT(11) DEFAULT NULL, empno int  not null, PRIMARY KEY (`id`), KEY `idx_dept_id` (`deptId`) #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `t_dept` (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;   INSERT INTO t_dept(deptName,address) VALUES('华山','华山'); INSERT INTO t_dept(deptName,address) VALUES('丐帮','洛阳'); INSERT INTO t_dept(deptName,address) VALUES('峨眉','峨眉山'); INSERT INTO t_dept(deptName,address) VALUES('武当','武当山'); INSERT INTO t_dept(deptName,address) VALUES('明教','光明顶'); INSERT INTO t_dept(deptName,address) VALUES('少林','少林寺');  INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('风清扬',90,1,100001); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('岳不群',50,1,100002); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('令狐冲',24,1,100003); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('洪七公',70,2,100004); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('乔峰',35,2,100005); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('灭绝师太',70,3,100006); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('周芷若',20,3,100007); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('张三丰',100,4,100008); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('张无忌',25,5,100009); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES('韦小宝',18,null,100010);    ALTER TABLE `t_dept` add  CEO  INT(11)  ; update t_dept set CEO=2 where id=1; update t_dept set CEO=4 where id=2; update t_dept set CEO=6 where id=3; update t_dept set CEO=8 where id=4; update t_dept set CEO=9 where id=5;    下面的emp 和 dept表是用存储过程向 emp表添加了50w条数据,dept添加了10w条数据

 

1),列出自己的掌门比自己年龄小的人员

select e.name,e.age,e1.name,e1.age from t_emp e  left join t_dept d on e.deptId = d.id left join t_emp e1 on d.ceo =e1.id where e.age > e1.age;

换成大表explain 优化查询

explain select e.name,e.age,e1.name,e1.age from emp e  left join dept d on e.deptId = d.id left join emp e1 on d.ceo =e1.id where e.age > e1.age;

 

2),列出所有年龄低于自己门派平均年龄的人员

-- 求出所有门派平均年龄 select deptId,avg(age) ag from t_emp group by deptId;  select e.name,e.age,e1.ag,e1.age from t_emp e inner join (select deptId,avg(age) ag,age from t_emp where deptId is not null group by deptId) e1 on e.deptId=e1.deptId where e.age < e1.age;

优化:

2-1), 执行explain 

explain select SQL_NO_CACHE e.name,e.age,e1.ag,e1.age from emp e  inner join (select deptId,avg(age) ag,age from emp where deptId is not null group by deptId) e1  on e.deptId=e1.deptId  where e.age < e1.age;

2-2), 为dept 表的 group by 后面字段增加索引

create index idx_emp_deptId on emp(deptId);

2-3),为emp表的 deptId 建立索引和 age有了范围查询索引也建立索引

create index idx_emp_deptIdAge on emp(deptId,age);

2-4),再次执行explain ,优化完成

 

3),列出至少有2个年龄大于40岁的成员的门派(少林寺有南少林和北少林索引分组时应该注意)

select d.deptName,d.id,count(1) from t_emp e  inner join t_dept d  on e.deptId = d.id  where e.age>40   group by d.deptName,d.id  having count(1) >= 2 ;

优化:

3-1),explain 查看 使用 STRAIGHT_JOIN 

explain  select d.deptName,d.id,count(1) from dept d  STRAIGHT_JOIN emp e   on e.deptId = d.id  where e.age>40   group by d.deptName,d.id  having count(1) >= 2 ;

​​​​​​STRAIGHT_JOIN 什么时候可以用? 

  1. 1明确STRAIGHT_JOIN的概念(指定谁是驱动表谁是被驱动表),例如 a表 STRAIGHT_JOIN  b表 这里 a表是驱动表 b表是北驱动表,注意 小表驱动大表

  2. 明确前后俩个表的数量级关系

3-2), 给 dept表的 再给 group by 字段建立索引,因为id 为主键索引不用添加索引

create index idx_dept_deptName on dept(deptName);

 3-3), 给emp表的deptId 和 age 也添加索引

create index idx_dept_deptIdAge on emp(deptId,age);

3-4),执行 explain 语句 优化完成

 

​​​​​​​4),至少有俩个非掌门人成员的门派

select d.deptName,d.id from t_emp e  inner join dept d1 on e.deptId = d1.id left join t_dept d on e.id = d.ceo where d.id is null group by d.deptName,d.id having count(1)>=2

优化:

4-1), explain 使用 STRAIGHT_JOIN 指定那个表为驱动表

explain select d.deptName,d.id from dept d1 STRAIGHT_JOIN emp e on e.deptId = d1.id left join dept d on e.id = d.ceo where d.id is null group by d.deptName,d.id having count(1)>=2

4-2),为 dept 表添加添加索引

create index idx_dept_deptName on dept(deptName); create index idx_dept_ceo on dept(ceo);

4-3),为 emp表添加索引

create index idx_emp_deptId on emp(deptId);

4-4),再次使用 explain 执行sql语句,优化完成

5),列出全部人员,并增加一列备注为 是否为 掌门人 如果是掌门人显示是 否则 否 条件是

 #方式1 select e.name,if(d.id is null ,'否','是') 是否为掌门人  from t_emp e  left join t_dept d  on e.id=d.ceo;  #方式2 select  e.name, case when d.id is null then '否' else '是' end 是否为掌门人  from t_emp e  left join t_dept d  on  e.id=d.ceo;	

 

6),列出全部门派,增加一列备注 老鸟or菜鸟 门派平均年龄大于50显示老鸟否则菜鸟

select d.deptName, if(avg(e.age)>50,'老鸟','菜鸟') 老鸟or菜鸟 from t_dept d  inner join t_emp e  on d.id=e.deptId  group by d.deptName,d.id

7),显示每个门派年龄最大的人

select e.name,e.age, max(age) mx from t_emp e  inner join  t_dept d  on e.deptId=d.id  group by d.deptName,d.id having mx

8),显示每个门派年龄第二大的人(sql编程)

set @rank =0; #定义变量 -排名 set @last_deptId=0; #下一个部门id set @last_age=0; #定义年龄变量  select e.name,e.age,e.deptId from ( select e.*, if( @last_deptId = deptId, if(@last_age = age,@rank,@rank:=@rank+1), @rank:=1) as rk, @last_deptId:=deptId as last_deptId, @last_age:=age as last_age from t_emp e order by e.deptId,e.age desc) e where e.rk =2;

 

需要了解更多数据库技术:mysql 经典面试题以及优化过程,都可以关注数据库技术分享栏目—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/dtteaching/814617.html

(0)
上一篇 2021年9月14日
下一篇 2021年9月14日

精彩推荐