前言
寫這篇文章主要是自覺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);