sql语句50题的个人解法+理解(MySQL)

前言

  写这篇文章主要是自觉sql方面的不足,在网上找练习题的时候找到了CSDN博主启明星的指引sql语句练习50题(Mysql版)这篇文章,感觉很有用。于是将自己做题的思路和思考记录下来,以备平常复习。本文也有参考其他文献,参考的文献网址也已记录下来。如果有新的解法或者自己思考的内容有错误,烦请大家能够补缺和指正。再次感谢启明星的指引和其他文献作者的贡献。
  本人用的是mysql的5.7版本。建表语句和数据放在最后的附录中。

练习题

1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数

解题关键思路:01课程和02课程的比较,说明score这个表需要使用2次,分别查询为01、02课程的分数;需要展示课程分数,说明返回的结果中需要分别将2个score表的分数返回

select s.*,a.score 01score,b.score 02score from student s 
join 
(select * from score sc where sc.cid="01") a
on s.sid=a.sid
join
(select * from score sc where sc.cid="02") b 
on a.sid=b.sid where a.score>b.score;

上述可以继续优化:

select s.*,a.score 01score,b.score 02score from student s 
join score a on s.sid=a.sid and a.cid="01"
join score b on s.sid=b.sid and b.cid="02" 
where a.score>b.score;

2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数

同上,只是比较成绩的时候用小于号

3、查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩

解题关键:使用groupby根据学生编号和学生姓名进行分组计算,再用having来比较大小

select s.sid,s.sname,AVG(score) avgscore from student s
join score sc on s.sid=sc.sid 
group by s.sid,s.sname having avgscore>=60;

小优化:可以用round()函数对小数点位数进行控制

4、查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩

同上,只是比较的时候用小于号

注意:上述的sql语句仅限于对有成绩的学生进行查询,如果将无成绩(学生没有选修该课程)视为小于60分,那么还需要将没有成绩的学生union进去

select s.sid,s.sname,AVG(score) avgscore from student s
join score sc on s.sid=sc.sid 
group by s.sid,s.sname having avgscore>=60
union
select s2.sid,s2.sname,0 avgscore from student s2
where s2.sid not in 
(select distinct(sid) from score);

5、查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩

select s.sid,s.sname,count(sc.cid) "选课门数",sum(sc.score) "各门总分" from student s join score sc on s.sid=sc.sid group by s.sid,s.sname;

6、查询"李"姓老师的数量

select count(1) from teacher where tname like '李%';

7、查询学过"张三"老师授课的同学的信息

select * from student where sid in (
  select distinct sid from score where cid=(
    select distinct cid from course where tid=(
      select tid from teacher where tname="张三")));

也可以将student和score两张表join起来

8、查询没学过"张三"老师授课的同学的信息

select * from student where sid not in (
  select distinct sid from score where cid=(
    select distinct cid from course where tid=(
      select tid from teacher where tname="张三")));

9、查询学过编号为"01"并且也学过编号为"02"的课程的同学的信息

下面是错误的写法:

select * from student where sid in(
select distinct(s.sid) from student s join score sc on s.sid=sc.sid 
where sc.cid in ('01','02'));

第二个in,即in (‘01’,‘02’),只要满足其中一个就会被选上,导致只学过01或02的学生也被选上,正确的做法是:

select s.* from student s 
join score b on s.sid = b.sid 
join score c on s.sid = c.sid where b.cid='01' and c.cid='02';

10、查询学过编号为"01"但是没有学过编号为"02"的课程的同学的信息

解题思路:不能单纯套用第九题,然后将c.cid='02’改为c.cid!=‘02’,这样无法排除b.cid='02’的情况。所以思路应该改为,先用student和score关联,筛选出cid='01’的数据,然后获取学过’02’课程的学生编号,只要再从筛选出来的数据中排除这些学生编号,剩下的就是满足题意的学生信息

select s.* from student s 
join score b on s.sid=b.sid where b.cid='01' and s.sid not in(
select c.sid from score c where c.cid='02');

还有另外的写法相对简单,不用进行表关联

select s.* from student s 
where s.sid in (select sid from score where cid='01' ) and s.sid not in(select sid from score where cid='02');

11、查询没有学全所有课程的同学的信息

解题思路:没有学全所有课程,意味着学生学习的课程数!=课程总数,难点在于怎么获取课程数、比较后还能获取到该学生的编号。提示:聚合函数不必与select语句一同使用!每个学生都要比较,所以要使用group by分组,并用having关键字进行分组后的条件比较!

select * from student where sid not in (
  select sc.sid from score sc group by sc.sid having 
  count(sc.cid) = (select count(c.cid) from course c));

一开始我是写成:select * from student where sid in ( select sc.sid from score sc group by sc.sid having count(sc.cid) < (select count(c.cid) from course c)),但对比其他人的写法之后发现一个问题,如果有学生一门都不选,那么这语句的结果集是没有包含这个学生的(因为没有数据的情况下,count函数是不会返回任何值,导致一门都不选的学生没有返回值而没有包含在in的选项中),所以需要改变思路,将学全的学生排除。

优化思路:用explain语句解析后发现上述的语句性能不是很好,对student表进行了全表扫描,对score表虽然使用了索引,但是也查询了所有的行,毕竟not in语句应该尽量少用。之所以用not in是为了解决有学生一门不选的问题,那么用左连接去保证student没有被遗漏就好了

select s.* from student s 
left join score sc on sc.sid=s.sid group by s.sid 
having count(sc.cid)<(select count(*) from course);

12、查询至少有一门课与学号为"01"的同学所学相同的同学的信息

select s.* from student s 
left join score sc1 on sc1.sid=s.sid
where sc1.cid in(
    select distinct(sc2.cid) from score sc2 where sc2.sid='01') 
    group by s.sid ;

另外一种写法:

select * from student where sid in(
	select distinct a.sid from score a where a.cid in(
        select a.cid from score a where a.sid='01'));

13、查询和"01"号的同学学习的课程完全相同的其他同学的信息

解题思路:再次强调,in语句只要满足其中一个条件就能被选上。我原本的思路是排除01没选择的课程的学生,排除一门都没选的学生,排除选择课程数小于01的学生。参考别人的写法后,有一种思路与我的类似,但是条件是:筛选出课程数=01的学生,再排除01没选择课程的学生,再排除01本身。sql如下:

select s.* from student s 
where s.sid in (
  select distinct(sid) from score group by sid 
    having count(cid)=(
    select count(cid) from score where sid='01'))
and s.sid not in (
  select distinct(sid) from score where cid not in (
    select cid from score where sid='01')
    group by sid)
and s.sid!='01';

在第9行的group by sid即使去掉似乎不影响结果,因为最外面的语句是单表查询,不会有笛卡尔积。

还有另外一种思路:用01选的课程编号拼成字符串与每个非01的学生的课程编号拼成的字符串进行比较,如果相同,说明是选择了同样的课程,sql如下:

SELECT t3.* FROM (
  SELECT sid, group_concat(cid ORDER BY cid) group1 FROM score WHERE sid <> '01' GROUP BY sid) t1
INNER JOIN (
 SELECT group_concat(cid ORDER BY cid) group2 FROM score WHERE sid = '01' GROUP BY sid) t2 
ON t1.group1 = t2.group2
INNER JOIN student t3 ON t1.sid = t3.sid;

14、查询没学过"张三"老师讲授的任一门课程的学生姓名

select sname from student where sid not in (
  select distinct(sid) from score where cid = (
    select cid from course where tid = (
      select tid from teacher where tname='张三')));

15、查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩

select s.sid,s.sname,round(avg(sc.score),2) avgscore from student s 
left join score sc on s.sid=sc.sid where s.sid in (
  select sid from score where score<60 group by sid having count(score)>=2)
group by s.sid,s.sname;

16、检索"01"课程分数小于60,按分数降序排列的学生信息

select s.* from student s join score sc on s.sid=sc.sid where sc.cid='01' and sc.score<60 order by sc.score desc;

17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩

解题思路:如果直接select *,avg(score)就会出现因行数不一致导致数据显示不全的情况。所以需要将每个课程都设为一列查询。解题的关键在于如何根据学生编号查出不同课程的成绩,并作为最终结果集的返回结果。这个需要将根据学生编号查出对应的成绩的子查询放在嵌套在select语句中

select sc.sid,(select score from score where sid=sc.sid and cid='01') '语文',
(select score from score where sid=sc.sid and cid='02') '数学',
(select score from score where sid=sc.sid and cid='03') '英语',
round(avg(sc.score),2) '平均分' from score sc 
group by sc.sid order by `平均分` desc;

注意书写的问题:在设置别名时,中文别名可以用单引号、双引号、`号括住。但在使用字段别名的时候,特别是中文字符,要用`符号,而不要用单引号或双引号,否则将有可能无法识别别名而不生效!特别像上例一样,如果order by的字段平均分用单引号或双引号都无法使该排序生效!

18.查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率–及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90

select c.cid '课程ID' ,c.cname '课程name',max(sc.score) '最高分', min(sc.score) '最低分', round(avg(sc.score),2) '平均分',
round(100*(sum(case when sc.score >=60 and sc.score<70 then 1 else 0 end)/count(sc.score)),2) '及格率',
round(100*(sum(case when sc.score >=70 and sc.score<80 then 1 else 0 end)/count(sc.score)),2) '中等率',
round(100*(sum(case when sc.score >=80 and sc.score<90 then 1 else 0 end)/count(sc.score)),2) '优良率',
round(100*(sum(case when sc.score >=90 then 1 else 0 end)/count(sc.score)),2) '优秀率'
from course c join score sc on sc.cid=c.cid group by c.cid,c.cname;

19、按各科成绩进行排序,并显示排名

太难了,完全没有思路,写出来的东西也是各种问题,网上的答案比较难理解。有两种解法,一种是常规写法,另一种比较高端,需要使用到变量。

方法1:

按各科成绩进行排序并没有难度,难点是如何显示排名。注意:在mysql中是没有rank函数的。如果在oracle中,这个题目是普通难度,而在mysql是地狱难度。

所谓的排名,看自己的成绩是否比别人高,如果比自己成绩高的人只有0个,说明自己是第一名;比自己高的有一个,说明自己是第二名,以此类推。

所以排名可以这么定义:rank=成绩比自己高的人数+1

要实现排名,对成绩按科目进行比较,此时需要两次使用到score表。

sql语句如下:

select * from score a left join score b on a.cid=b.cid and a.score<b.score order by a.sid,a.cid;

执行后的结果截取部分如下:

可以看出学生01三科都最高,故没有一个人成绩比他高,学生02在学科01中有三个人比他高,以此类推,这样就可以开始做下一步的排名了。

注意,关联两张表的条件是a.cid=b.cid而不是a.sid=b.sid,因为成绩是根据学科对比,而不是根据学生对比,如果是后者会消除笛卡尔积,就无法根据学科进行成绩对比。这里需要用cid关联起成绩!这点很重要,否则整个sql都会有错。另外,使用左连接是确保最高成绩能够保留,这点也很重要。还有一点,groupby的条件是a.sid和a.cid两个,保证每个学生的每科成绩都显示,缺一不可。

接下来就是排名了,使用groupby统计排序即可,sql如下:

①并列名次没有空缺的写法:

select a.*,count(a.score) rank from score a left join score b on a.cid=b.cid and a.score<b.score group by a.sid,a.cid order by a.cid,rank;

在这里插入图片描述

②并列名次有空缺的写法:

之所以第一个写法并列名次没有空缺,原因是左连接时,如果单科排名第一,那么右表会出现null值。如果根据左表的统计数量进行排名,那么排名是连续的(左表不会有null)。如果要有空缺,那么对右表的统计数量进行排名就好了。当然需要+1(因为null值统计的结果为0嘛)

select a.*,count(b.score)+1 rank from score a left join score b on a.cid=b.cid and a.score<b.score group by a.sid,a.cid order by a.cid,rank;

在这里插入图片描述

③同名次有空缺且按sid排序(题目的最终写法):

第二种写法之所以有并列名次,原因在于没有对相同的成绩进行处理,增加条件对相同成绩按照sid排序即可

select a.*,count(b.score)+1 rank from score a left join score b on a.cid=b.cid and (a.score<b.score or (a.score=b.score and a.sid>b.sid)) group by a.sid,a.cid order by a.cid,rank;

在这里插入图片描述

参考文献:https://blog.csdn.net/Athain/article/details/84944151

方法2:

下面的写法对题目的理解与第一种写法的不一致。第一种写法是根据学科对成绩进行分组排序,而下面的写法是无视学科分组,直接对所有成绩进行排序。虽然理解不同,但其写法值得学习:

select a.sid,a.cid,
        @i:=@i +1 as 保留排名,
        @k:=(case when @score=a.score then @k else @i end) as rank不保留排名,
        @score:=a.score as score
    from (
        select sid,cid,score from score GROUP BY sid,cid,score ORDER BY score DESC
)a,(select @k:=0,@i:=0,@score:=0)s;

暂时没有找到合适的解释。个人的理解是:定义了3个变量,分别为@i,@k,@score。@i定义为自增1,@k定义为当成绩相等时取自身,其余情况取@i,@score定义为成绩。整个sql的意思:首先对score表进行分组排序,得到一个临时表a,然后全表扫描时每读取一行,@i的值都会+1,@score都会读取出当前的a.score,@k的值会判断是本身还是@i,全表读完之后就能获取到根据成绩排序的排名。值得注意的是,from语句除了a表,还有一个s表,这个s表用来初始化3个变量。如果不关联s表进行初始化,那么当多次执行后,3个变量的值会一直累加!

20、查询学生的总成绩并进行排名

根据19题的方法二,比较容易,只要将@score变量的值改为总成绩就可以了

select a.sid,
        @i:=@i +1 as 保留排名,
        @k:=(case when @score=a.sumscore then @k else @i end) as 同分不保留排名,
        @score:=a.sumscore as score
    from (
        select sid,sum(score) sumscore from score GROUP BY sid ORDER BY sumscore DESC
)a,(select @k:=0,@i:=0,@score:=0)s;

在这里插入图片描述

注意:如果是同分的情况,貌似sid越小,排名越高(猜测是由于a表是按照成绩排序,根据排序结果,越先读取到的数据@i越前,所以导致排名也越高,未论证)

21、查询不同老师所教不同课程平均分从高到低显示

select c.cid,t.tname `教师名称`,round(avg(sc.score),2) `平均分` from course c left join score sc on c.cid=sc.cid join teacher t on c.tid=t.tid group by c.cid,t.tname order by `平均分` DESC;

22、查询所有课程的成绩第2名到第3名的学生信息及该课程成绩

方法1:需要使用变量获得排名,然后获取第2、3名。但缺点比较明显,需要手动指定cid,且同分数的不做并列处理。

select s.*,a.cid,a.score from student s join 
(select sid,cid,score,@i:=@i+1 rank from score,(select @i:=0) i where cid='01' order by score desc) a on s.sid=a.sid where a.rank in(2,3) 
union
select s.*,b.cid,b.score from student s join 
(select sid,cid,score,@k:=@k+1 rank from score,(select @k:=0) k where cid='02' order by score desc) b on s.sid=b.sid where b.rank in(2,3) 
union
select s.*,c.cid,c.score from student s join 
(select sid,cid,score,@l:=@l+1 rank from score,(select @l:=0) l where cid='03' order by score desc) c on s.sid=c.sid where c.rank in(2,3) ;

方法2:利用19题的方法一获得排名,以返回表作为临时表,再进行筛选。注意,这里要用left join,以保证按照临时表的顺序,如果用inner join,则会出现顺序打乱的情况。这种写法可以解决手动指定cid的问题。如果要对并列处理,使用19题的方法一中其他的处理sql即可

select s.*,c.cid,c.score from 
(select a.*,count(b.score)+1 rank from score a left join score b on a.cid=b.cid and (a.score<b.score or (a.score=b.score and a.sid>b.sid)) group by a.sid,a.cid order by a.cid,rank) c left join student s on s.sid=c.sid where c.rank in (2,3);

23、统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[0-60]及所占百分比

select c.cid,c.cname,a.s1 `[100-85]人数`,a.s2 `[85-70]人数`,a.s3 `[70-60]人数`,a.s4 `[0-60]人数`,
a.r1 `[100-85]%`,a.r2 `[85-70]%`,a.r3 `[70-60]%`,a.r4 `[0-60]%` from
(select cid,
sum(case when score>=85 and score<=100 then 1 else 0 end) s1,
round(sum(case when score>=85 and score<=100 then 1 else 0 end)/count(score)*100,2) r1,
sum(case when score>=70 and score<85 then 1 else 0 end) s2,
round(sum(case when score>=70 and score<85 then 1 else 0 end)/count(score)*100,2) r2,
sum(case when score>=60 and score<70 then 1 else 0 end) s3,
round(sum(case when score>=60 and score<70 then 1 else 0 end)/count(score)*100,2) r3,
sum(case when score>=0 and score<=60 then 1 else 0 end) s4 ,
round(sum(case when score>=0 and score<=60 then 1 else 0 end)/count(score)*100,2) r4 from score group by cid) a
left join course c on a.cid = c.cid;

24、查询学生平均成绩及其名次

select s.sid,s.sname,a.`平均分`, @i:=@i+1 rank from student s join 
(select sid,round(avg(score),2) `平均分` from score group by sid order by `平均分` desc) a on a.sid=s.sid,(select @i:=0) i

如果不需查询学生的信息,可以不用关联student表

25、查询各科成绩前三名的记录

select a.*,count(b.score)+1 rank from score a left join score b on a.cid=b.cid and (a.score<b.score or (a.score=b.score and a.sid>b.sid)) group by a.sid,a.cid having rank in (1,2,3) order by a.cid,rank;

26、查询每门课程被选修的学生数

select cid,count(sid) `选修人数` from score group by cid;

27、查询出只有两门课程的全部学生的学号和姓名

select sid,sname from student where sid in (
    select sid from score group by sid having count(cid)=2);

28、查询男生、女生人数

select sex,count(sid) `学生人数` from student group by sex;

29、查询名字中含有"风"字的学生信息

select * from student where sname like '%风%';

30、查询同名同性学生名单,并统计同名人数

没想明白题目的意思,以为是同姓,还在想怎么分割姓,口吐芬芳orz,直接上参考sql

select a.* ,count(*) `同名人数` from student a join student b on a.sid!=b.sid and a.sname=b.sname and a.sex=b.sex group by a.sid,a.sname,a.sex;

注意:为了统计人数,需要用到groupby语句

31、查询1990年出生的学生名单

select * from student where birth like '1990%';

32、查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列

select cid,round(avg(score),2) `平均分` from score group by cid order by `平均分` desc,cid;

严谨一点的话,最后的排序cid应该加上asc,不过默认排序是asc,写不写不影响

33、查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩

select s.sid,s.sname,round(avg(sc.score),2) avgscore from score sc left join student s on s.sid=sc.sid group by s.sid having avgscore>=85;

34、查询课程名称为"数学",且分数低于60的学生姓名和分数

select s.sname, sc.score from student s join score sc on s.sid=sc.sid join course c on sc.cid=c.cid where c.cname='数学' and sc.score<60;

select s.sname, sc.score from student s join score sc on s.sid=sc.sid where sc.score<60 and cid=(
select cid from course where cname='数学');

35、查询所有学生的课程及分数情况;

题目说的不清楚,改成“查询所有学生每科成绩和总分”

难点:一开始我写的sql语句是

select s.sid,s.sname,
	(case when sc.cid='01' then sc.score else 0 end) `语文`,
	(case when sc.cid='02' then sc.score else 0 end) `数学`,
	(case when sc.cid='03' then sc.score else 0 end) `英语`,
	sum(score) `总分`
	from score sc left join student s on sc.sid=s.sid group by s.sid

发现只有语文有成绩,而数学和英语全部为0。去掉group by和总分后,执行的结果如下

在这里插入图片描述

问题到了怎么把三行合成一行,一下子没想出来,再看看网上的解法,发现可以用sum语句…

select s.sid,s.sname,
	sum(case when sc.cid='01' then sc.score else 0 end) `语文`,
	sum(case when sc.cid='02' then sc.score else 0 end) `数学`,
	sum(case when sc.cid='03' then sc.score else 0 end) `英语`,
	sum(sc.score) `总分`
	from score sc left join student s on sc.sid=s.sid group by s.sid,sname;

36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数;

select s.sname,c.cname,sc.score from score sc join student s on sc.sid=s.sid 
join course c on sc.cid=c.cid where sc.score>=70;

37、查询不及格的课程

题目说的不清晰,按照以下理解分为不同解法:

1、查询有不及格的学生的编号、姓名、成绩和课程名称:

select s.sid,s.sname,c.cname,sc.score from score sc join student s on sc.sid=s.sid join course c on sc.cid=c.cid where sc.score<60;

2、查询有学生不及格的课程编号及课程名称,可以加上不及格学生的人数统计

select c.cid,c.cname,count(sc.sid) `不及格人数` from course c join score sc on c.cid=sc.cid where sc.score<60 group by c.cid;

38、查询课程编号为01且课程成绩在80分以上的学生的学号和姓名

select s.sid,s.sname from student s join score sc on s.sid=sc.sid where sc.cid='01' and sc.score>=80;

39、求每门课程的学生人数

select cid,count(*) `选修人数` from score group by cid;

40、查询选修"张三"老师所授课程的学生中,成绩最高的学生信息及其成绩

select s.*,c.score from (
select a.*,count(b.score)+1 rank from score a left join score b on a.cid=b.cid and (a.score<b.score or (a.score=b.score and a.sid>b.sid)) group by a.sid,a.cid order by a.cid,rank) c 
join student s on s.sid=c.sid join course co on co.cid=c.cid join teacher t on co.tid=t.tid where t.tname='张三';

41、查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩

我自己的解法:

select a.sid asid,a.cid acid,b.sid bsid,b.cid bcid,b.score score from score a join score b where a.sid!=b.sid and a.cid!=b.cid and a.score=b.score group by a.sid,a.cid;

注意要用groupby语句将重复的数据去掉

还有一种解法:

select DISTINCT b.sid,b.cid,b.score from score a,score b where a.cid != b.cid and a.score = b.score;

用distinct去掉重复数据,这里select语句中用a表和b表都没问题,只是用a表没有按照sid递增的顺序排列(原因未明)

42、查询每门功成绩最好的前两名

跟25题的解法一致,只是我不想把排名显示出来,所以在外面又套了一层select

select c.sid,c.cid,c.score from (
select a.*,count(b.score)+1 rank from score a left join score b on (a.score<b.score or (a.score=b.score and a.sid>b.sid)) and a.cid=b.cid group by a.sid,a.cid order by a.cid,rank) c
where c.rank in (1,2)

还有一种解法:

select a.sid,a.cid,a.score from score a where 
(select COUNT(1) from score b where b.cid=a.cid and b.score>=a.score)<=2 
    ORDER BY a.cid;
-- 或者下面这个更加容易理解
select a.sid,a.cid,a.score from score a where 
(select COUNT(1) from score b where b.cid=a.cid and b.score>a.score)<2 
ORDER BY a.cid

这种解法果然很牛逼,最外层的where语句参考19题中关于rank的理解,rank=成绩比自己高的人数+1,那么前两名可以这么理解:每科成绩比自己高的的人数不能超过2个人!这刚好就是最外层where语句的成立条件!!至于order by语句,只是为了让输出的结果更好看的附加条件,不是必要的

只是对执行流程有点难理解。个人理解应该是先拿a表和b表一起计算出count,得到where条件,再从a表中筛选出所需数据

43、统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列

select cid,count(sid) `选修人数` from score group by cid having `选修人数`>5 order by `选修人数` desc,cid asc

44、检索至少选修两门课程的学生学号

select sid from score group by sid having count(cid)>=2

45、查询选修了全部课程的学生信息

select * from student where sid in (
select sid from score group by sid having count(cid)=(
select count(cid) from course))

46、查询各学生的年龄 按照出生日期来算,当前月日 < 出生年月的月日则,年龄减一

这题需要用到DATE_FORMAT()函数和CASE WHEN THEN ELSE END语句。

SELECT sid,DATE_FORMAT(NOW(),'%Y')-DATE_FORMAT(birth,'%Y')-(case when DATE_FORMAT(NOW(),'%m%d')-DATE_FORMAT(birth,'%m%d')>0 then 0 else 1 end) age from student

或者

SELECT sid,DATE_FORMAT(NOW(),'%Y')-DATE_FORMAT(birth,'%Y')-(case when DATE_FORMAT(NOW(),'%m%d')>DATE_FORMAT(birth,'%m%d') then 0 else 1 end) age from student

吐槽一句,这种明明可以在应用层用逻辑判断就能简单实现的功能为何要浪费数据库的运算能力==

47、查询本周过生日的学生

原本我是写成select * from student where DATE_FORMAT(birth,'%V')=DATE_FORMAT(now(),'%V'),但是%V有这样的含义:如果当年1月1号不是星期天的话,那么1月1日不是当年的第一周!而是上一年的最后一周!

应该改用%U或%u(对应一周的第一天为周日或周一)

select * from student where DATE_FORMAT(birth,'%U')=DATE_FORMAT(now(),'%U')

不过从%U的定义来看,其实也有点逻辑上的缺点。它将每一年的第一个星期都视为第0周。从运算的角度来看,这个避免了%V的首周问题,但从实际理解来说%U得出的结果+1才是我们实际理解的周数。不过这是小问题可以通过+1来解决。

参考网址的解法:

使用自带的WEEK(date[,mode])函数和YEARWEEK(date[,mode])函数。(就是这么简单==)两个函数同样有像%U和%V的首周问题,注意mode参数的选取对结果的选取。

week()返回周数,而yearweek()返回年份及周数

select * from student where WEEK(DATE_FORMAT(NOW(),'%Y%m%d'))=WEEK(birth);select * from student where YEARWEEK(birth)=YEARWEEK(DATE_FORMAT(NOW(),'%Y%m%d'));

附带对week()函数的讲解:https://www.yiibai.com/mysql/week.html

48、查询下周过生日的学生

select * from student where DATE_FORMAT(birth,'%U')=DATE_FORMAT(now(),'%U')+1

select * from student where WEEK(DATE_FORMAT(NOW(),'%Y%m%d'))+1 =WEEK(s_birth)

49、查询本月过生日的学生

SELECT * from student where DATE_FORMAT(birth,'%m')=DATE_FORMAT(NOW(),'%m')

50、查询下月过生日的学生

SELECT * from student where DATE_FORMAT(birth,'%m')-DATE_FORMAT(NOW(),'%m')=1

完结散花~

附录:建表语句和原始数据

CREATE TABLE `Student`(
	`sid` VARCHAR(20),
	`sname` VARCHAR(20) NOT NULL DEFAULT '',
	`birth` VARCHAR(20) NOT NULL DEFAULT '',
	`sex` VARCHAR(10) NOT NULL DEFAULT '',
	PRIMARY KEY(`sid`)
);

CREATE TABLE `Course`(
	`cid`  VARCHAR(20),
	`cname` VARCHAR(20) NOT NULL DEFAULT '',
	`tid` VARCHAR(20) NOT NULL,
	PRIMARY KEY(`cid`)
);

CREATE TABLE `Teacher`(
	`tid` VARCHAR(20),
	`tname` VARCHAR(20) NOT NULL DEFAULT '',
	PRIMARY KEY(`tid`)
);

CREATE TABLE `Score`(
	`sid` VARCHAR(20),
	`cid`  VARCHAR(20),
	`score` INT(3),
	PRIMARY KEY(`sid`,`cid`)
);

insert into Student values('01' , '赵雷' , '1990-01-01' , '男');
insert into Student values('02' , '钱电' , '1990-12-21' , '男');
insert into Student values('03' , '孙风' , '1990-05-20' , '男');
insert into Student values('04' , '李云' , '1990-08-06' , '男');
insert into Student values('05' , '周梅' , '1991-12-01' , '女');
insert into Student values('06' , '吴兰' , '1992-03-01' , '女');
insert into Student values('07' , '郑竹' , '1989-07-01' , '女');
insert into Student values('08' , '王菊' , '1990-01-20' , '女');

insert into Course values('01' , '语文' , '02');
insert into Course values('02' , '数学' , '01');
insert into Course values('03' , '英语' , '03');


insert into Teacher values('01' , '张三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');


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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章