以下語句由PostgreSQL數據庫語法編寫,都全部調試過,歡迎探討~部分語句結尾未添加分號,時間關系大小寫不統一,格式不是特別整潔,強迫症求放過~
先創建四張表格
create table student( s_id varchar(10), s_name varchar(20), s_age date, s_sex varchar(10) ); create table course( c_id varchar(10), c_name varchar(20), t_id varchar(10) ); create table teacher ( t_id varchar(10), t_name varchar(20) ); create table score ( s_id varchar(10), c_id varchar(10), score integer , );
插入數據
insert into student (s_id, s_name, s_age, s_sex) values ('01' , '趙雷' , '1990-01-01' , '男'), ('02' , '錢電' , '1990-12-21' , '男'), ('03' , '孫風' , '1990-05-20' , '男'), ('04' , '李雲' , '1990-08-06' , '男'), ('05' , '周梅' , '1991-12-01' , '女'), ('06' , '吳蘭' , '1992-03-01' , '女'), ('07' , '鄭竹' , '1989-07-01' , '女'), ('08' , '王菊' , '1990-01-20' , '女'); insert into course (c_id, c_name, t_id) values ('01' , '語文' , '02'), ('02' , '數學' , '01'), ('03' , '英語' , '03'); insert into teacher (t_id, t_name) values ('01' , '張三'), ('02' , '李四'), ('03' , '王五'); insert into score (s_id, c_id, score) values ('01' , '01' , 80), ('01' , '02' , 90), ('01' , '03' , 99), ('02' , '01' , 70), ('02' , '02' , 60), ('02' , '03' , 80), ('03' , '01' , 80), ('03' , '02' , 80), ('03' , '03' , 80), ('04' , '01' , 50), ('04' , '02' , 30), ('04' , '03' , 20), ('05' , '01' , 76), ('05' , '02' , 87), ('06' , '01' , 31), ('06' , '03' , 34), ('07' , '02' , 89), ('07' , '03' , 98);
值得注意的是,學生的名單跟成績名單相比,學生的id是多的,也就是說8號學生是沒有成績的,另外也發現有的學生的部分課程沒有成績,這都是需要注意的,
1、查詢"01"課程比"02"課程成績高的學生的學號及課程分數(重點)
解題思路:
第一步:score中分別查詢出01課程的成績和02的課程的成績進行inner join,
(select s_id, score as score1 from score where c_id='01') as a inner join (select s_id, score as score2 from score where c_id='02') as b
on a.s_id =b.s_id
第二步:以01課程成績>02課程成績進行條件篩選
where score1 > score2
第三步:選出需要的字段
select course.s_id, score1,score2
按照語句書寫順序將以上語句組織為:
select a.s_id as s_id,score1,score2 from (select s_id, score as score1 from score where c_id='01') a inner join (select s_id, score as score2 from score where c_id='02') b on a.s_id=b.s_id where score1>score2;
1.1 查詢同時存在" 01 "課程和" 02 "課程的情況
select * from (select s_id, score as score1 from score where c_id='01') a inner join (select s_id, score as score2 from score where c_id='02') b on a.s_id=b.s_id
1.2 查詢存在" 01 "課程但可能不存在" 02 "課程的情況(不存在時顯示為 null )
select * from (select s_id, score as score1 from score where c_id='01') a left join (select s_id, score as score2 from score where c_id='02') b on a.s_id=b.s_id
1.3 查詢不存在" 01 "課程但存在" 02 "課程的情況
select * from (select s_id, score as score1 from score where c_id='01') a right join (select s_id, score as score2 from score where c_id='02') b on a.s_id=b.s_id where score1 is null
2、查詢平均成績大於60分的學生的學號和平均成績(重點)
SELECT s_id,AVG(score) FROM score GROUP BY s_id having avg(score)>60
3、查詢所有同學的學號、姓名、選課數、總成績
簡潔的寫法
SELECT t1.s_id,t1.s_name,count(t1.s_id),sum(t2.score) FROM student AS t1 LEFT JOIN score as t2 ON t1.s_id = t2.s_id GROUP BY t1.s_id,t1.s_name
優化版: SELECT t1.s_id,t1.s_name,count(t1.s_id),sum(case when t2.score is NULL then 0 else t2.score end) FROM student AS t1 LEFT JOIN score as t2 ON t1.s_id = t2.s_id GROUP BY t1.s_id,t1.s_name
注意:這和代碼有bug。王菊同學的選課數目應該為0
一個冗長的答案:
SELECT t1.s_id,t1.s_name,t2.cnt,t2.total_score FROM (SELECT s_id,s_name FROM student ) AS t1 LEFT JOIN (SELECT score.s_id,COUNT(*) AS cnt,SUM(score) AS total_score FROM score GROUP BY score.s_id) AS t2 ON t1.s_id = t2.s_id
解題思路:先將student表和score表進行聯結,這里選左聯結,確保找到所有學生的信息,然后對新表進行匯總和計算,group by中只能出現select中出現的字段喲~
第四題:查詢姓“李”的老師的個數;
SELECT count(*) from teacher where t_name like '李%'
第五題:查詢沒學過“張三”老師課的學生的學號、姓名(重點)
思路:
SELECT DISTINCT s_id,s_name FROM student WHERE s_id NOT IN (SELECT s_id FROM score as d LEFT JOIN course AS b ON d.c_id = b.c_id LEFT JOIN teacher AS c ON b.t_id = c.t_id WHERE c.t_name = '張三')
常見的錯誤示范:,相當於把張三老師的記錄去掉了,實際上會選出錯誤的結果
select distinct a.s_id,a.s_name from student as a LEFT JOIN score as d on a.s_id = d.s_id left join course as b on d.c_id = b.c_id left join teacher as c on b.t_id = c.t_id WHERE c.t_name <> '張三'
第六題:查詢學過“張三”老師所教的所有課的同學的學號、姓名(重點)
select distinct a.s_id,a.s_name from student as a LEFT JOIN score as d on a.s_id = d.s_id left join course as b on d.c_id = b.c_id left join teacher as c on b.t_id = c.t_id WHERE c.t_name = '張三'
SELECT DISTINCT s_id,s_name FROM student WHERE s_id IN (SELECT s_id FROM score as d LEFT JOIN course AS b ON d.c_id = b.c_id LEFT JOIN teacher AS c ON b.t_id = c.t_id WHERE c.t_name = '張三')
SELECT s_id,s_name from student where s_id in( SELECT s_id from score where c_id in(SELECT c_id from course where t_id in (SELECT t_id from teacher where t_name = '張三')))
7、查詢學過編號為“01”的課程並且也學過編號為“02”的課程的學生的學號、姓名(重點)
SELECT DISTINCT s_id,s_name FROM student WHERE s_id IN (SELECT DISTINCT t1.s_id FROM (SELECT s_id,score FROM score WHERE c_id = '01') as t1 JOIN (SELECT s_id,score FROM score WHERE c_id = '02') as t2 ON t1.s_id = t2.s_id )
解題思路:在這里首先是利用score表字查詢的自聯結將原來表中的列變成了行
8、查詢課程編號為“02”的總成績(不重點)
SELECT sum(score) from score where c_id = '02'
9、查詢所有課程成績小於60分的學生的學號、姓名(同第2題,要注意存在成績為null的情況,因此得采用not in)
思路解讀:因為這樣子查詢中的結果不會包含null的id,整體-不符合要求 = 符合要求的+null
select s_id,s_name from student where s_id not in (select s_id from score GROUP BY s_id having max(score)>= 60);
錯誤寫法:從score中直接選id的話,這種未包含全部學生的id,成績為null的情況 select s_id,s_name from student where s_id in (select s_id from score GROUP BY s_id having max(score) < 60);
10、查詢沒有學全所有課的學生的學號、姓名(重點)
select a.s_id,a.s_name from student as a left join score as d on a.s_id = d.s_id left join course as b on b.c_id = d.c_id GROUP BY a.s_id,a.s_name having count (a.s_id)<(SELECT count(distinct c_id) from course)
注意:此處不用distinct也可
錯誤的寫法:從score中選會漏掉選課結果為null的王菊,但是因為course表沒有跟student表直接管理,因此最好的辦法是join
select s_id,s_name from student where s_id in (SELECT s_id,count(s_id) from score GROUP BY s_id having count(s_id) < SELECT count(*) from course))
11、查詢至少有一門課與學號為“01”的學生所學課程相同的學生的學號和姓名(重點)
inner join的效率比in高一些
一種復雜的寫法:
SELECT t1.s_id,t1.s_name from student as t1 right join (SELECT DISTINCT s_id from score where c_id in (SELECT c_id from score where s_id = '01' )and s_id != '01') as t2 on t1.s_id = t2.s_id
注意;通過一對多方式來選擇時會出現很多重復數據,這里指,一個s_id 對應多個c_id,一對一聯結是比較好的,根據c_id來選s_id的時候,必然會發生重復,去重用distinct
SELECT s_id,s_name from student where s_id in (SELECT DISTINCT s_id from score where c_id in (SELECT c_id from score where s_id = '01') and s_id != '01')
12.查詢和“01”號同學所學課程完全相同的其他同學的學號(重點)
復雜寫法
select s_id from score where c_id = ( SELECT c_id from score where s_id = '01') and s_id != '01' GROUP BY s_id having COUNT(s_id) = (SELECT COUNT(*) from score where s_id = '01')
簡潔寫法:
SELECT s_id from score group by s_id having (count(s_id) = (SELECT COUNT(*) from score where s_id = '01') ) and (s_id != '01')
注意:本道題因為課程一共有三項,1號同學正好有三項課程,因此指判斷課程數是否相同即可,但是如果總課程數大於3以上方法則不適用,上述方法存在bug,以下應該是正確寫法
注意:雙重否定的用法,not in (a,b,c) 的意思跟 in(全部集合中除去(a.b.c)的集合)的意思一樣
如果用集合的概念來解釋就是 如果兩個不同的集合a和b相加等於全集U,那么針對全集中的元素來說not in a = in b
SELECT s_id from score where s_id not in ( select s_id from score where c_id not in ( SELECT c_id from score where s_id = '01') )and s_id != '01'GROUP BY s_id having COUNT(s_id) = (SELECT COUNT(*) from score where s_id = '01')
13.查詢沒學過"張三"老師講授的任一門課程的學生姓名(重點)
SELECT DISTINCT s_id,s_name from student where s_id not in (select s_id from score where c_id in (SELECT c_id from course where t_id in (select t_id from teacher where t_name = '張三')))
14.
15.查詢兩門及其以上不及格課程的同學的學號,姓名及其平均成績(重點)
select a.s_id,a.s_name,t.avg_score from student as a INNER JOIN (SELECT s_id,AVG(score)as avg_score from score GROUP BY s_id having COUNT(*) >=2 and max(score)< 60) as t on a.s_id = t.s_id
16.檢索"01"課程分數小於60,按分數降序排列的學生信息(和34題重復,不重點)
SELECT * from student where s_id in
(SELECT s_id from score where c_id = '01' and score < 60 ORDER BY score);
17.按平均成績從高到低顯示所有學生的所有課程的成績以及平均成績(重重點)
注意;這里的max起到取出數據的作用,沒有實際的max功能,分別進行操作的時候,可以用case when
SELECT s_id, max (case when c_id = '02' then score else null end) as "數學", max (case when c_id = '01' then score else null end) as "語文", max (case when c_id = '03' then score else null end) as "英語", AVG(score) from score GROUP BY s_id ORDER BY AVG(score) desc
錯誤示范:這種形式不是最優解
SELECT a.s_id,d.score,AVG(d.score) FROM student as a LEFT JOIN score as d on a.s_id = d.s_id GROUP BY a.s_id,d.score ORDER BY AVG(d.score) desc
18.查詢各科成績最高分、最低分和平均分:以如下形式顯示:課程ID,課程name,最高分,最低分,平均分,及格率,中等率,優良率,優秀率
SELECT c_id,max (score),min (score),avg(case when score is not null then score else 0 end), AVG(case when score >= 70 and score < 80 then 1 else 0 end) as middle, AVG(case when score >= 80 and score < 90 then 1 else 0 end) as good, AVG(case when score >= 90 then 1 else 0 end) as great from score GROUP BY c_id;
19、按各科成績進行排序,並顯示排名(重點dens_rank()over(order by 列)
SELECT s_id,c_id,score, DENSE_RANK()OVER(PARTITION BY c_id ORDER BY score desc) AS ranking from score
窗口函數的用法看這里:看這里


20、查詢學生的總成績並進行排名(不重點)
SELECT s_id,sum(score) from score group by s_id order by sum(score) desc
21、查詢不同老師所教不同課程平均分從高到低顯示(不重點)
SELECT c.t_name,d.c_id,avg(d.score) from score as d inner join course as b on d.c_id = b.c_id inner join teacher as c on c.t_id = b.t_id group by d.c_id,c.t_name order by avg(d.score)desc
22、查詢所有課程的成績第2名到第3名的學生信息及該課程成績(重要 25類似)
SELECT c_id,s_name,score from student as a INNER join (SELECT *, DENSE_RANK()OVER(PARTITION BY c_id ORDER BY score desc) AS ranking from score) as t on a.s_id = t.s_id where ranking = 2 or ranking = 3 ORDER BY c_id,score des
23、使用分段[100-85],[85-70],[70-60],[<60]來統計各科成績,分別統計各分數段人數:課程ID和課程名稱(重點和18題類似)
select co.c_id ,c_name, sum (case when score < 60 then 1 else 0 end ) as D, sum (case when score >= 60 and score < 70 then 1 else 0 end ) as C, sum (case when score >= 70 and score < 85 then 1 else 0 end ) as B, sum (case when score >= 85 and score < 100 then 1 else 0 end ) as A from score as sc left join course as co on co.c_id = sc.c_id GROUP BY co.c_id ,c_name
24、查詢學生平均成績及其名次(同19題,重點)
注意在:DENSE_RANK()OVER( ORDER BY avg_score desc) AS ranking中對成績按照desc進行排序
在結尾對ranking升序
SELECT * , DENSE_RANK()OVER( ORDER BY avg_score desc) AS ranking from (SELECT s_id,AVG(case when score is not null then score else 0 end) as avg_score FROM score GROUP BY s_id) as t ORDER BY ranking
考慮成績並列可以用dense_rank(),不考慮成績並列可以用row_number()
25、查詢各科成績前三名的記錄(不考慮成績並列情況)(重點 與22題類似)
SELECT c_id,s_name,score from student as a INNER join (SELECT *, ROW_NUMBER()OVER(PARTITION BY c_id ORDER BY score desc) AS ranking from score) as t on a.s_id = t.s_id where ranking = 1 or ranking = 2 or ranking = 3 ORDER BY c_id,score desc

如果需要知道課程名稱,可以繼續join一個course表
26、查詢每門課程被選修的學生數(不重點)
SELECT c_id,count(*) from score GROUP BY c_id
27、 查詢出只有兩門課程的全部學生的學號和姓名(不重點)
SELECT s_id,s_name from student where s_id in (SELECT s_id from score GROUP BY s_id having count(*) = 2)
28、查詢男生、女生人數(不重點)
select s_sex,count(s_sex) from student group by s_sex;
29、查詢名字中含有"風"字的學生信息(不重點)
select * from student where s_name like '%風'
30、
31、查詢1990年出生的學生名單(重點year)
select * from student where s_age like '1900%';
32、查詢平均成績大於等於85的所有學生的學號、姓名和平均成績(不重要)
select a.s_id,a.s_name,avg(case when d.score is not null then d.score else 0 end) from student as a left join score as d on a.s_id = d.s_id group by a.s_id,a.s_name having avg(d.score) > 85;
33、查詢每門課程的平均成績,結果按平均成績升序排序,平均成績相同時,按課程號降序排列(不重要)
select c_id ,avg (score) from score group by c_id order by avg (score) asc,c_id desc
34、查詢課程名稱為"數學",且分數低於60的學生姓名和分數(不重點)
select a.s_name,d.score,d.c_id from student as a inner join score as d on a.s_id = d.s_id where c_id in (select c_id from course where c_name = '數學') and d.score < 60
35、查詢所有學生的課程及分數情況(重點)
SELECT a.s_id,a.s_name, sum(case when d.c_id = '01' then d.score else null end ) as sub_01, sum(case when d.c_id = '02' then d.score else null end ) as sub_02, sum(case when d.c_id = '03' then d.score else null end ) as sub_03 from student as a left join score as d on a.s_id = d.s_id group by a.s_id,a.s_name
錯誤滴答案:
SELECT a.s_id ,a.s_name,d.score from student as a left join score as d on a.s_id = d.s_id
36、查詢任何一門課程成績在70分以上的姓名、課程名稱和分數(重點)
正常答案:
select a.s_name,b.c_name,d.score from student as a inner join score as d on a.s_id = d.s_id inner join course as b on d.c_id = b.c_id where d.score > 70
一個比較復雜又沒必要的答案:
select a.s_name,b.c_name,d.score from student as a inner join score as d on a.s_id = d.s_id inner join course as b on d.c_id = b.c_id GROUP BY a.s_name,b.c_name,d.score having MIN(d.score) > 70 ORDER BY a.s_name;
37、查詢不及格的課程並按課程號從大到小排列(不重點)
select d.c_id, b.c_name ,score from score as d inner join course as b on d.c_id = b.c_id where score < 60 order by d.c_id DESC
38、查詢課程編號為03且課程成績在80分以上的學生的學號和姓名(不重要)
select a.s_id,a.s_name from student as a left join score as d on a.s_id = d.s_id where c_id = '03' and score > 80;
39、求每門課程的學生人數(不重要)
select c_id , count(*) from score GROUP BY c_id
40、查詢選修“張三”老師所授課程的學生中成績最高的學生姓名及其成績(重要limit)
解法1:
SELECT s_name,score FROM student as a inner join score as d on a.s_id = d.s_id where c_id in (select c_id from course where t_id in(select t_id from teacher where t_name = '張三')) ORDER BY score desc LIMIT 1
解法2:
SELECT s_name,score FROM student as a inner join score as d on a.s_id = d.s_id inner join course as b on d.c_id = b.c_id inner join teacher as c on b.t_id = c.t_id where t_name = '張三' ORDER BY score desc LIMIT 1
解法3: select s_name,score from (SELECT s_name,score,dENSE_RANK()OVER(ORDER BY score desc) AS ranking FROM student as a inner join score as d on a.s_id = d.s_id inner join course as b on d.c_id = b.c_id inner join teacher as c on b.t_id = c.t_id where t_name = '張三' ORDER BY score desc) as t where ranking = 1
41、查詢不同課程成績相同的學生的學生編號、課程編號、學生成績 (重點)
網上答案,我認為這個答案也不夠嚴謹,因為學生可能兩個成績相同也可能三個成績相同,
select s1.s_id,s1.c_id,s1.score,s2.score,s3.score from (select s_id,c_id,score from score where c_id = '01' ) as s1 inner join (select s_id,c_id,score from score where c_id = '02' ) as s2 on s1.s_id = s2.s_id inner join (select s_id,c_id,score from score where c_id = '02' ) as s3 on s2.s_id = s3.s_id where s1.score = s2.score and s2.score = s3.score
我認為我這個是正確答案
select s_id,c_id,score from score where s_id not in (select s_id from score group by s_id having max(score)!= min(score))
42、查詢每門功成績最好的前兩名(同22和25題)
SELECT c_id,s_name,score from student as a INNER join (SELECT *, DENSE_RANK()OVER(PARTITION BY c_id ORDER BY score desc) AS ranking from score) as t on a.s_id = t.s_id where ranking = 1 or ranking = 2 ORDER BY c_id,score desc
43、統計每門課程的學生選修人數(超過5人的課程才統計)。要求輸出課程號和選修人數,查詢結果按人數降序排列,若人數相同,按課程號升序排列(不重要)
select c_id,COUNT(*) from score GROUP BY c_id having COUNT(*) > 5 order by COUNT(*) desc ,c_id asc
44、檢索至少選修兩門課程的學生學號(不重要)
select s_id,COUNT(*) from score GROUP BY s_id having COUNT(*) >= 2
45、查詢選修了全部課程的學生信息(重點)
select * from student where s_id in (select s_id from score group by s_id having count(DISTINCT c_id) = (select count(*) from course))
47、查詢沒學過“張三”老師講授的任一門課程的學生姓名
select s_id,s_name from student where s_id not in (SELECT s_id from score where c_id in (select c_id from course where t_id in (select t_id from teacher where t_name = '張三')))
48、查詢兩門以上不及格課程的同學的學號及其平均成績
select s_id,avg(score) from score group by s_id having COUNT(s_id) >= 2 and max(score) < 60
select s_id,avg(score) from score where score < 60 group by s_id having COUNT(s_id) >= 2
49、查詢各學生的年齡(精確到月份)
備注:年份轉換成月份,比如結果是1.9,ditediff 最后取1年
SELECT s_id,s_age, extract(YEAR from age(current_date,s_age)) from student
時間函數用法看這里:https://www.yiibai.com/html/postgresql/2013/080783.html
50、查詢本月過生日的學生
SELECT * from student where EXTRACT(month from s_age) = EXTRACT(month from CURRENT_DATE)
50.1、查詢下月過生日的學生
注意where子句中,兩個select 子句部分都要各自加括號,這樣括號內先計算得出一個具體的數值
SELECT * from student where (SELECT extract (month from s_age)) = (SELECT extract(month from CURRENT_DATE) +1)
50.2、查詢本周過生日的學生
50.3、查詢下周過生日的學生
因為postgresql中沒有week函數,暫時還不知道怎么寫
未完待續~因為還不太會優化sq代碼,日后再標注出比較好的解答





