使用mysql的5.7.23版本
mysql数据准备:
建表
/*
-- 建表
-- 学生表
CREATE TABLE `Student`(
`s_id` VARCHAR(20),
`s_name` VARCHAR(20) NOT NULL DEFAULT '',
`s_birth` VARCHAR(20) NOT NULL DEFAULT '',
`s_sex` VARCHAR(10) NOT NULL DEFAULT '',
PRIMARY KEY(`s_id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 课程表
CREATE TABLE `Course`(
`c_id` VARCHAR(20),
`c_name` VARCHAR(20) NOT NULL DEFAULT '',
`t_id` VARCHAR(20) NOT NULL,
PRIMARY KEY(`c_id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 教师表
CREATE TABLE `Teacher`(
`t_id` VARCHAR(20),
`t_name` VARCHAR(20) NOT NULL DEFAULT '',
PRIMARY KEY(`t_id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 成绩表
CREATE TABLE `Score`(
`s_id` VARCHAR(20),
`c_id` VARCHAR(20),
`s_score` INT(3),
PRIMARY KEY(`s_id`,`c_id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
向表中插入数据
准备产生随机数函数:
-- 产生随机整数(0-100)
delimiter $
create function ran_num() returns int(5)
begin
declare i int default 0;
set i =floor( rand()*100 ) ;
return i ;
end $
-- 插入学生表测试数据
/*
delimiter $
create procedure insert_stu(in dno_start int(10) ,in data_times int(10))
begin
declare i int default 0;
set autocommit = 0 ;
repeat
insert into Student values(CONCAT('0',i) ,CONCAT('学生0',i),CONCAT('199',mod(i,10),'-0',mod(i,10),'-0',mod(i,10)),mod(i,2)) ;
set i=i+1 ;
until i=data_times
end repeat ;
commit ;
end $
delimiter ;
call insert_stu(0,1000) ;*/
-- 课程表测试数据
/*
delimiter $
create procedure insert_course(in dno_start int(10) ,in data_times int(10))
begin
declare i int default 0;
set autocommit = 0 ;
repeat
insert into Course values(CONCAT('0',i), CONCAT('课程0',i) , CONCAT('0',i));
set i=i+1 ;
until i=data_times
end repeat ;
commit ;
end $
delimiter ;
call insert_course(0,10) ;*/
-- 教师表测试数据
/*
delimiter $
create procedure insert_teacher(in dno_start int(10) ,in data_times int(10))
begin
declare i int default 0;
set autocommit = 0 ;
repeat
insert into Teacher values(CONCAT('0',i) ,CONCAT('老师0',i)) ;
set i=i+1 ;
until i=data_times
end repeat ;
commit ;
end $
delimiter ;
call insert_teacher(0,10) ;*/
-- 成绩表测试数据
/*
delimiter $
create procedure insert_score(in dno_start int(10) ,in data_times int(10))
begin
declare i int default 0;
declare j int default 0;
set autocommit = 0 ;
repeat
set j=0;
repeat
insert into Score values(CONCAT('0',i) , CONCAT('0',j) , ran_num());
set j=j+1 ;
until j=10
end repeat ;
set i=i+1 ;
until i=data_times
end repeat ;
commit ;
end $
delimiter ;
call insert_score(0,1000) ;
*/
如果出现错误:Lock wait timeout exceeded; try restarting transaction 点击该链接
表中数据量:
SELECT count(1) from score; -- 10000
SELECT count(1) from student; -- 1000
SELECT count(1) from course; -- 10
SELECT count(1) from teacher;-- 10
1、查询"01"课程比"02"课程成绩高的学生的信息及01和02课程分数
-- ans1:
SELECT stu.* ,t.score1,t.score2 from Student stu ,(
SELECT s1.s_id,s1.s_score score1,s2.s_score score2 from (SELECT s_id,s_score from Score where c_id='01') as s1,(SELECT s_id,s_score from Score where c_id='02') as s2
where s1.s_score>s2.s_score and s1.s_id=s2.s_id) as t
where stu.s_id=t.s_id;
-- ans2:
SELECT s.*,t.score1,t.score2 from
(SELECT s1.s_id,s1.s_score score1,s2.s_score score2 from (SELECT s_id,s_score from Score where c_id='01') as s1,(SELECT s_id,s_score from Score where c_id='02') as s2
where s1.s_score>s2.s_score and s1.s_id=s2.s_id) as t LEFT JOIN student s on t.s_id=s.s_id ;
-- ans3:
select a.* ,b.s_score as 01_score,c.s_score as 02_score from
Student a
join score b on a.s_id=b.s_id and b.c_id='01'
left join score c on a.s_id=c.s_id and c.c_id='02' or c.c_id = NULL where b.s_score>c.s_score;
show profiles;
-- 494条数据
下面是show profiles的结果
3条查询耗时差距不大
2.查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩
-- ans1
SELECT st.s_id,st.s_name,avgs from (SELECT ROUND(AVG(s_score),2) avgs,s_id from Score GROUP BY s_id HAVING avgs>=60 ) as t
LEFT JOIN Student st on t.s_id=st.s_id;
-- ans2
select b.s_id,b.s_name,ROUND(AVG(a.s_score),2) as avg_score from
student b
join score a on b.s_id = a.s_id
GROUP BY b.s_id,b.s_name HAVING ROUND(AVG(a.s_score),2)>=60;
show profiles;
-- 129条数据
show profiles;
两个sql的查询耗时有数量级的差距
SHOW STATUS LIKE '%Handler_read_%';
ans1如图
ans2如图
可能原因:
ans1通过子查询过滤了数据,使得两个表join的笛卡尔积数量减少(mysql执行顺序from on join group by having select)
3.查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩-- (包括有成绩的和无成绩的)
-- ans1
SELECT st.s_id,st.s_name,avgs from (SELECT ROUND(AVG(s_score),2) avgs,s_id from Score GROUP BY s_id HAVING avgs<60 ) as t
LEFT JOIN Student st on t.s_id=st.s_id
UNION
SELECT s_id,s_name,0 from student st where s_id not in(SELECT s_id from score );
-- ans2
SELECT st.s_id,st.s_name,avgs from (SELECT ROUND(AVG(s_score),2) avgs,s_id from Score GROUP BY s_id HAVING avgs<60 ) as t
LEFT JOIN Student st on t.s_id=st.s_id
UNION
SELECT s_id,s_name,0 from student st where not exists(SELECT s_id from score s where s.s_id=st.s_id );
-- ans3
select b.s_id,b.s_name,ROUND(AVG(a.s_score),2) as avg_score from
student b
left join score a on b.s_id = a.s_id
GROUP BY b.s_id,b.s_name HAVING ROUND(AVG(a.s_score),2)<60
union
select a.s_id,a.s_name,0 as avg_score from
student a
where a.s_id not in (
select distinct s_id from score);
show profiles;
-- 871
ans1的in 和ans2的exists有数量级区别的的duration (in>>exists) 不知道为什么???
如果mysql更喜欢小表驱动大表(score表的数量>>student) in 是里面驱动外面,exists是主查询驱动子查询,与结果不一致
4.查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩
-- ans1
SELECT st.s_id,st.s_name,t.counts,t.avgs from student st ,
(SELECT s_id, count(c_id) as counts, SUM(s_score) as avgs from score GROUP BY s_id) as t
where st.s_id=t.s_id;
-- ans2
select a.s_id,a.s_name,count(b.c_id) as sum_course,sum(b.s_score) as sum_score from
student a
left join score b on a.s_id=b.s_id
GROUP BY a.s_id,a.s_name;
show profiles;
-- 1000
show profiles;
结果:sql执行耗时数量级差别(ans1的子查询group by优于ans2主查询的group by,ans2先笛卡尔积group by的时候数据量比ans1的数据量少(少了几列)???)