本系列文章:
  MySQL(一)SQL语法、数据类型、常用函数、事务
  MySQL(二)MySQL SQL练习题
  MySQL(三)视图、存储过程、索引
  MySQL(四)存储引擎、锁
  MySQL(五)MySQL架构、数据库优化、主从复制
  MySQL(六)SQL语句优化
  MySQL(七)MySQL和Oracle、PostgreSQL的区别

一、创建测试表

  • 1、用到的几张表
      假设有下面几张表:
  1. 学生表
    Student(s_id,s_name,s_birth,s_sex) --学生编号,学生姓名, 出生年月,学生性别
  2. 课程表
    Course(c_id,c_name,t_id) --课程编号, 课程名称, 教师编号
  3. 教师表
    Teacher(t_id,t_name) --教师编号,教师姓名
  4. 成绩表
    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实现。
Logo

更多推荐