文章目录
本系列文章:
MySQL(一)SQL语法、数据类型、常用函数、事务
MySQL(二)MySQL SQL练习题
MySQL(三)视图、存储过程、索引
MySQL(四)存储引擎、锁
MySQL(五)MySQL架构、数据库优化、主从复制
MySQL(六)SQL语句优化
MySQL(七)MySQL和Oracle、PostgreSQL的区别
一、创建测试表
- 1、用到的几张表
假设有下面几张表:
- 学生表
Student(s_id,s_name,s_birth,s_sex) --学生编号,学生姓名, 出生年月,学生性别- 课程表
Course(c_id,c_name,t_id) --课程编号, 课程名称, 教师编号- 教师表
Teacher(t_id,t_name) --教师编号,教师姓名- 成绩表
Score(s_id,c_id,s_score) --学生编号,课程编号,分数
- 2、建表语句
#学生表
CREATE TABLE IF NOT EXISTS Student(
s_id VARCHAR(20),
s_name VARCHAR(20) NOT NULL,
s_birth VARCHAR(20) NOT NULL,
s_sex VARCHAR(10) NOT NULL,
PRIMARY KEY(s_id)
);
#课程表
CREATE TABLE IF NOT EXISTS Course(
c_id VARCHAR(20),
c_name VARCHAR(20) NOT NULL,
t_id VARCHAR(20) NOT NULL,
PRIMARY KEY(c_id)
);
#教师表
CREATE TABLE IF NOT EXISTS Teacher(
t_id VARCHAR(20),
t_name VARCHAR(20) NOT NULL,
PRIMARY KEY(t_id)
);
#成绩表
CREATE TABLE IF NOT EXISTS Score(
s_id VARCHAR(20),
c_id VARCHAR(20),
s_score INT(3),
PRIMARY KEY(s_id,c_id)
);
- 3、插入测试数据
#插入学生表测试数据
insert into Student values('01' , 'zhaolei' , '1990-01-01' , 'man');
insert into Student values('02' , 'qiandian' , '1990-12-21' , 'man');
insert into Student values('03' , 'sunfeng' , '1990-05-20' , 'man');
insert into Student values('04' , 'liyun' , '1990-08-06' , 'man');
insert into Student values('05' , 'zhoumei' , '1991-12-01' , 'women');
insert into Student values('06' , 'wulan' , '1992-03-01' , 'women');
insert into Student values('07' , 'zhengzhu' , '1989-07-01' , 'women');
insert into Student values('08' , 'wangju' , '1990-01-20' , 'women');
#课程表测试数据
insert into Course values('01' , 'yuwen' , '02');
insert into Course values('02' , 'shuxue' , '01');
insert into Course values('03' , 'yingyu' , '03');
#教师表测试数据
insert into Teacher values('01' , 'zhangsan');
insert into Teacher values('02' , 'lisi');
insert into Teacher values('03' , 'wangwu');
#成绩表测试数据
insert into Score values('01' , '01' , 80);
insert into Score values('01' , '02' , 90);
insert into Score values('01' , '03' , 99);
insert into Score values('02' , '01' , 70);
insert into Score values('02' , '02' , 60);
insert into Score values('02' , '03' , 80);
insert into Score values('03' , '01' , 80);
insert into Score values('03' , '02' , 80);
insert into Score values('03' , '03' , 80);
insert into Score values('04' , '01' , 50);
insert into Score values('04' , '02' , 30);
insert into Score values('04' , '03' , 20);
insert into Score values('05' , '01' , 76);
insert into Score values('05' , '02' , 87);
insert into Score values('06' , '01' , 31);
insert into Score values('06' , '03' , 34);
insert into Score values('07' , '02' , 89);
insert into Score values('07' , '03' , 98);
- 4、此时表中的数据
学生表中的数据:
课程表中的数据:
教师表中的数据:
成绩表中的数据:
二、SQL语句练习题
- 1、查询名字中含有"feng"字的学生信息
select * from student where s_name like '%feng%';
结果:
- 2、查询"li"姓老师的数量
select count(*) from teacher where t_name like 'li%';
结果:
- 3、查询男生、女生人数
select s_sex as '性别',count(s_sex) as '数量' from student group by s_sex;
结果:
- 4、求每门课程的学生人数
如果只是把每门课程的学生人数统计出来,直接group by进行分组就行。
select c_id as '课程',count(s_id) as '数量' from score group by c_id;
结果:
如果要把课程的详细信息打印出来,就需要关联查询。
select c.c_id ,c.c_name as '课程名称',count(c.c_id) as '学生数量' from course c
join score on c.c_id = score.c_id
group by c.c_id;
结果:
- 5、查询下周过生日的学生
week表示一年中的第几周
select * from student
where week(date_format(now(),'%Y%m%d'))+1 = week(s_birth);
- 6、查询本月过生日的学生
#与上一题类似的解法
select * from student
where month(date_format(now(),'%Y%m%d')) = month(s_birth);
- 7、查询下月过生日的学生
select * from student
where month(date_format(now(),'%Y%m%d'))+1 = month(s_birth);
结果:
- 8、查询1990年出生的学生名单
select s_name,s_birth from student where s_birth like '1990%';
结果:
- 9、检索至少选修两门课程的学生学号
select s_id,count(c_id) from Score
group by s_id having count(c_id) >= 2;
结果:
- 10、查询选修了全部课程的学生信息
select * from Student
where s_id in (select s_id from Score group by s_id
having count(c_id) = (select count(*) from course));
结果:
- 11、查询出只有两门课程的全部学生的学号和姓名
select s_id,s_name from student
where s_id in(select s_id from score group by s_id
having count(c_id)=2);
结果:
- 12、查询不同课程、成绩相同的学生的学生编号、课程编号、学生成绩
select distinct sc2.*
from score sc1,score sc2
where sc1.c_id != sc2.c_id and sc1.s_score = sc2.s_score;
结果:
- 13、查询学过编号为"01"并且也学过编号为"02"的课程的同学的信息
查询结果,只需要从一张表中取出,但却需要从别的表中进行条件判断时,就from 多张表;如果查询结果需要从多张表中取,就关联查询。
select stu.* from student stu,score sc1,score sc2
where stu.s_id = sc1.s_id
and stu.s_id = sc2.s_id
and sc1.c_id='01' and sc2.c_id='02';
结果:
- 14、查询学过编号为"01",但是没有学过编号为"02"的课程的同学的信息
难点在于用s_id去score表中查找对应的条件。
select stu.* from student stu
where stu.s_id in (select s_id from score where c_id='01' )
and stu.s_id not in(select s_id from score where c_id='02');
结果:
- 15、查询"01"课程比"02"课程成绩高的学生的信息及课程分数
从查询结果来看,要进行关联查询,同时又要进行两门课程的成绩比较,这不免要把成绩表使用两次,也就是关联两次。
select stu.* ,sco.s_score as 01_score,sco2.s_score as 02_score
from student stu
join score sco on stu.s_id = sco.s_id and sco.c_id='01'
join score sco2 on stu.s_id=sco2.s_id and sco2.c_id='02'
where sco.s_score>sco2.s_score;
结果:
- 16、查询"01"课程比"02"课程成绩低的学生的信息及课程分数
select stu.* ,sco.s_score as 01_score,sco2.s_score as 02_score
from student stu
join score sco on stu.s_id = sco.s_id and sco.c_id='01'
join score sco2 on stu.s_id=sco2.s_id and sco2.c_id='02'
where sco.s_score < sco2.s_score;
结果:
- 17、查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩
查询结果来自多张表,说明要做关联查询
select sc.s_id,stu.s_name,avg(sc.s_score) as avg_score
from score sc
join student stu on sc.s_id = stu.s_id
group by sc.s_id
having avg(sc.s_score) >= 60;
结果:
- 18、查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩 (包括有成绩的和无成绩的)
平均分小于60的并不难查询,从上一题就能反向推出。接下来要考虑的是没有成绩的怎么查询,其实就是s_id不在成绩表中。查询到两个结果集后,用union求并集即可。
select stu.s_id,stu.s_name,round(avg(sc.s_score),2) as avg_score
from student stu
join score sc
on stu.s_id = sc.s_id
group by stu.s_id,stu.s_name
having round(avg(sc.s_score),2)<60
union
select stu.s_id,stu.s_name,0 as avg_score from student stu
where stu.s_id not in (select distinct s_id from score);
结果:
- 19、查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩
从结果来看,需要关联查询,再加上一个分组即可。
select stu.s_id,
stu.s_name,
count(sc.c_id) as sum_course,
sum(sc.s_score) as sum_score
from student stu
left join score sc
on stu.s_id = sc.s_id
group by stu.s_id;
结果:
- 20、查询学过"zhangsan"老师授课的同学的信息
这个题要从多张表中进行反复查询,第二种比较容易想到。
select stu.* from student stu
join score sco
on stu.s_id = sco.s_id
where sco.c_id in(
select c_id from course where t_id =(
select t_id from teacher where t_name = 'zhangsan'
)
);
SELECT st.* from student st
left join score sc on sc.s_id=st.s_id
left join course c on c.c_id=sc.c_id
left join teacher t on t.t_id=c.t_id
where t.t_name='zhangsan';
结果:
- 21、查询没学过"zhangsan"老师授课的同学的信息
根据老师姓名找t_id,再根据t_id找c_id。找到c_id之后,将学生表和成绩表关联,找出听过张三课程的学生 信息,再用not in反向查找。
select * from student c
where c.s_id not in(
select a.s_id from student a
join score b on a.s_id = b.s_id where b.c_id in(
select c_id from course where t_id = (
select t_id from teacher where t_name = 'zhangsan'
)
)
);
结果:
22、查询没有学全所有课程的同学的信息
select s.* from student s
where s.s_id in(
select s_id from score where s_id not in(
select a.s_id from score a
join score b on a.s_id = b.s_id and b.c_id='02'
join score c on a.s_id = c.s_id and c.c_id='03'
where a.c_id='01'
)
)
结果:
- 23、查询至少有一门课与学号为"01"的同学所学相同的同学的信息
select * from student where s_id in(
select distinct a.s_id
from score a
where a.c_id in (
select a.c_id from score a where a.s_id='01'
)
);
结果:
- 24、查询和"01"号的同学学习的课程完全相同的其他同学的信息
select a.* from student a where a.s_id in(
select distinct s_id from score
where s_id!='01' and c_id in(
select c_id from score where s_id='01'
)
group by s_id
having count(1) = (
select count(1) from score where s_id='01'
)
);
结果:
- 25、查询没学过"zhangsan"老师讲授的任一门课程的学生姓名
select a.s_name from student a
where a.s_id not in (
select s_id from score where c_id = (
select c_id from course where t_id = (
select t_id from teacher where t_name = 'zhangsan'
)
)
group by s_id
);
结果:
- 26、查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
select a.s_id,a.s_name,round(avg(b.s_score)) from student a
left join score b on a.s_id = b.s_id
where a.s_id in(
select s_id from score where s_score<60
group by s_id having count(1)>=2
)
group by a.s_id,a.s_name
结果:
- 27、检索"01"课程分数小于60,按分数降序排列的学生信息
select a.*,b.c_id,b.s_score from student a,score b
where a.s_id = b.s_id and b.c_id='01' and b.s_score<60
order by b.s_score desc;
结果:
- 28、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
select a.s_id,
(select s_score from score where s_id=a.s_id and c_id='01') as 语文,
(select s_score from score where s_id=a.s_id and c_id='02') as 数学,
(select s_score from score where s_id=a.s_id and c_id='03') as 英语,
round(avg(s_score),2) as 平均分
from score a
group by a.s_id
order by 平均分 desc;
结果:
- 29、查询不同老师所教不同课程平均分从高到低显示
select a.t_id,c.t_name,a.c_id,round(avg(s_score),2) as avg_score
from course a
left join score b on a.c_id=b.c_id
left join teacher c on a.t_id=c.t_id
group by a.c_id,a.t_id,c.t_name
order by avg_score desc;
结果:
- 30、查询各科成绩前三名的记录
select sc1.s_id,sc1.c_id,sc1.s_score from score sc1
left join score sc2 on sc1.c_id = sc2.c_id and sc1.s_score<sc2.s_score
group by sc1.s_id,sc1.c_id,sc1.s_score
having count(sc2.s_id)<3
order by sc1.c_id,sc1.s_score desc;
结果:
- 31、查询同名同性学生名单,并统计同名人数
select stu1.s_name,stu1.s_sex,count(*)
from student stu1
join student stu2 on stu1.s_id !=stu2.s_id
and stu1.s_name = stu2.s_name
and stu1.s_sex = stu2.s_sex
group by stu1.s_name,stu1.s_sex;
- 32、查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列
select c_id,avg(s_score) avg_sc from score
group by c_id
order by avg_sc desc,c_id asc;
结果:
- 33、查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩
select stu.s_name,stu.s_name,avg(sc.s_score) avg_sc
from student stu
join score sc on stu.s_id=sc.s_id
group by sc.s_id
having avg_sc >=85;
结果:
- 34、查询课程名称为"数学",且分数低于60的学生姓名和分数
select stu.s_name,sc.s_score from student stu
join score sc on stu.s_id=sc.s_id
join course co on co.c_id=sc.c_id
where co.c_name = 'shuxue' and sc.s_score <60;
结果:
- 35、查询所有学生的课程及分数情况
SELECT st.s_id,st.s_name
,MAX(CASE WHEN co.c_name='yuwen' THEN sc.s_score ELSE NULL END) AS 'yuwen'
,MAX(CASE WHEN co.c_name='shuxue' THEN sc.s_score ELSE NULL END) AS 'shuxue'
,MAX(CASE WHEN co.c_name='yingyu' THEN sc.s_score ELSE NULL END) AS 'yingyu'
FROM student AS st
LEFT JOIN score AS sc ON st.s_id=sc.s_id
LEFT JOIN course AS co ON sc.c_id=co.c_id
GROUP BY st.s_id,st.s_name;
结果:
- 36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数
select stu.s_name,co.c_name,sc.s_score from course co
left join score sc on co.c_id = sc.c_id
left join student stu on stu.s_id=sc.s_id
where sc.s_score>=70
结果:
- 37、查询不及格的课程(不及格的成绩表中的信息+课程名称)
select sc.*,co.c_name from score sc
left join course co on sc.c_id = co.c_id
where sc.s_score<60
结果:
- 38、查询课程编号为01且课程成绩在80分以上的学生的学号和姓名
select st.s_id,st.s_name from student st
join score sc
on sc.s_id = st.s_id and sc.c_id='01' and sc.s_score>=80;
select st.s_id,st.s_name from student st
join score sc on sc.s_id = st.s_id
where sc.c_id='01' and sc.s_score>=80;
结果:
- 39、查询选修"zhangsan"老师所授课程的学生中,成绩最高的学生信息及其成绩
下面的解法比较容易想得到,就是一直关联表,最后加上条件。
SELECT stu.*,sc.s_score FROM student as stu
JOIN score as sc on stu.s_id=sc.s_id
JOIN course as co on sc.c_id=co.c_id
JOIN teacher as te ON te.t_id=co.t_id
WHERE te.t_name='zhangsan'
ORDER BY s_score DESC LIMIT 0,1;
结果:
- 40、查询每门功成绩最好的前两名
用两张成绩表,统计每门课中,比某个成绩高的成绩的数量,如果<=2,就说明这是前两名的数据,取出即可。
select sc1.s_id,sc1.c_id,sc1.s_score from score sc1
where (
select count(s_score) from score sc2
where sc2.c_id = sc1.c_id and sc2.s_score >= sc1.s_score
) <= 2
order by sc1.c_id;
结果:
- 41、统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
select c_id,count(s_id) num from Score
group by c_id
having num >5
order by num desc,c_id asc;
结果:
- 42、查询各学生的年龄,按照出生日期来算,当前月日 < 出生年月的月日则,年龄减一
select s_birth,
(DATE_FORMAT(NOW(),'%Y') - DATE_FORMAT(s_birth,'%Y') -
(case when DATE_FORMAT(NOW(),'%m%d') > DATE_FORMAT(s_birth,'%m%d')
then 0 else 1 end
)
) as age
from student;
结果:
- 43、查询本周过生日的学生
select * from student
where week(date_format(now(),'%Y%m%d')) = week(s_birth);
- 44、行转列、列转行
行转列可以通过case…when实现,列转行可以通过union实现。