一、表說明(MYSql)
二、導入測試數據
學生表
insert into Student values('01' , N'趙雷' , '1990-01-01' , N'男'); insert into Student values('02' , N'錢電' , '1990-12-21' , N'男'); insert into Student values('03' , N'孫風' , '1990-05-20' , N'男'); insert into Student values('04' , N'李雲' , '1990-08-06' , N'男'); insert into Student values('05' , N'周梅' , '1991-12-01' , N'女'); insert into Student values('06' , N'吳蘭' , '1992-03-01' , N'女'); insert into Student values('07' , N'鄭竹' , '1989-07-01' , N'女'); insert into Student values('08' , N'王菊' , '1990-01-20' , N'女');
課程表
insert into Course values('01' , N'語文' , '02'); insert into Course values('02' , N'數學' , '01'); insert into Course values('03' , N'英語' , '03');
教師表
insert into Teacher values('01' , N'張三'); insert into Teacher values('02' , N'李四'); insert into Teacher values('03' , N'王五');
成績表
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' , 99.5); insert into Score values('03' , '01' , 80); insert into Score values('03' , '02' , 99); 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);
(interview1)
三、查詢
成績情況
目錄:
1. 查詢各科成績第一名的記錄
2. 查詢各科成績前三名的記錄(x,y)
3. 查詢所有學生的課程及分數情況
4. 按平均成績從高到低顯示所有學生的所有課程的成績以及平均成績
5. 查詢學生平均成績及其名次
6. 查詢平均成績大於等於85的所有學生的學號、姓名和平均成績
7. 查詢平均成績小於60分的同學的學生編號和學生姓名和平均成績
8. 查詢每門課程的平均成績,結果按平均成績降序排列,平均成績相同時,按課程編號升序排列
9. 查詢所有同學的學生編號、學生姓名、選課總數、所有課程的總成績
10. 查詢不及格的課程
11. 查詢兩門及其以上不及格課程的同學的學號,姓名及其平均成績
12. 查詢在score表中不存在成績的學生信息的SQL語句/查詢沒有參加任何一門課程考試(缺考所有課程)的學生信息的SQL語句。
13. 查詢課程名稱為"數學",且分數低於60的學生姓名和分數
14. 檢索"01"課程分數小於60,按分數降序排列的學生信息
15. 查詢課程編號為01且課程成績在80分以上的學生的學號和姓名
16. 查詢選修"張三"老師所授課程的學生中,成績最高的學生信息及其成績
17. 查詢s_id=7的學生的平均分
18. 查詢任何一門課程成績在70分以上的學生姓名、課程名稱和分數
19. 查詢"01"課程比"02"課程成績高的學生的信息及課程分數
20. 查詢不同課程成績相同的學生的學生編號、課程編號、學生成績
-----------------------------------------------------------------------------------------------
1.查詢各科成績第一名的記錄
select student.s_id,sname,cname,m.score from student,course, ( select t1.* , (select count(distinct t2.score) from SCore t2 where t2.C_id = t1.C_id and t2.score >= t1.score)px from score t1 ) m where px between 1 and 1 and student.s_id=m.s_id and course.c_id=m.c_id order by m.C_id , m.px
分析:該查詢的關鍵是在socre表中查詢某分數在其所在課程的排名,如下面的一條記錄:
那么這條記錄中的80分在課程編號1中排第幾名呢?——這就是本條SQL查詢的關鍵問題。這個關鍵問題體現在SQL:
SQL1
select t1.* , (select count(distinct t2.score) from SCore t2 where t2.C_id = t1.C_id and t2.score >= t1.score)px from score t1
其查詢結果如下:
圖1
其中px列為其所在記錄在其所屬課程中的排名。那么這個排名是如何統計出來的呢?再看上面的SQL,從結構上看,SQL1對score表查詢了2次,關鍵是其中里面的查詢,其結果是count(distinct t2.score),count函數是查詢符合條件的條數,distinct 是去掉重復的;再結合條件C_id = t.C_id,這在查詢中會返回:t2與當前記錄行中t1的C_id相等的條數(去重后的)--舉例解釋:下面將SQL1簡化一下:
SQL2
select t1.* , (select count(distinct t2.score) from SCore t2 where t2.C_id = t1.C_id)px from score t1
查詢結果(片段):
上面的結果中的px列的值的意思是c_id=1的記錄有5條(去重后的,如果不去重,在score表中c_id=1的記錄有6條)。
現在再加上SQL1中的條件score >= t.score,這樣在查詢中就會返回:在t2與當前記錄行中t1的C_id相等的記錄中,比當前記錄行中t1的score大或者相等的條數——如圖1中的這條記錄:
此記錄中的1即是這條記錄中的score在c_id=1的課程中的排名。那么它是如何得來的呢?SQL1的查詢過程中首先會返回兩張虛擬表:t2與t1,如在查詢第一行記錄時,首先很容易地得到t1的第一行記錄,在查詢px的第一行記錄時,分析器會分析其中的where條件:首先得到與t1的第一行記錄中c_id相等(c_id=1)的集合,然后在此集合中查出比t1的第一行記錄中的score大於或等於的記錄,最后統計其條數(1),就是第一行記錄中px值。
小結:在SQL1的整個查詢中,t2所充當的角色相當於條件,而t1則是要返回的主要結果,因此可以稱t2為“條件表”,t1為“結果表”,前者的作用在於為后者提供比對的數據,二者的查詢結果一致。
結果:
2.查詢各科成績前三名的記錄
2.1分數重復時保留名次空缺(x)
select m.* , n.C_id , n.score from Student m, SCore n where m.S_id = n.S_id and n.score in (select score from score where C_id = n.C_id order by score desc ) order by n.C_id , n.score desc
2.2分數重復時不保留名次空缺,合並名次
select student.s_id,sname,cname,m.score from student,course,(select t.* , (select count(distinct score) from SCore where C_id = t.C_id and score >= t.score)px from score t) m where px between 1 and 3 and student.s_id=m.s_id and course.c_id=m.c_id order by m.C_id , m.px
結果:
3.查詢所有學生的課程及分數情況
select Student.* , Course.Cname , SCore.C_id , SCore.score from Student, SCore , Course where Student.S_id = SCore.S_id and SCore.C_id = Course.C_id group by Student.S_id , SCore.C_id order by Student.S_id , SCore.C_id
4.按平均成績從高到低顯示所有學生的所有課程的成績以及平均成績
注:1.ifnull(cast(avg(b.score) asdecimal(18,2)),0) 平均分 可以去掉ifnull,寫成:cast(avg(b.score) as decimal(18,2)) 平均分
2.目前在score表中,如果某位同學沒有參加考試則沒有成績記錄,這樣會導致計算平均分錯誤,如鄭竹同學的平均分只計算了其(數學+英語)/2。正確的做法應該是即使某位同學沒有參加某科目的考試,那么該同學在socre表也應有記錄,只不過對應的記錄中的成績為0,這樣計算平均分就不會有錯。
select a.S_id 學生編號 , a.Sname 學生姓名 , max(case c.Cname when '語文' then b.score else null end) 語文, max(case c.Cname when '數學' then b.score else null end) 數學, max(case c.Cname when '英語' then b.score else null end) 英語, ifnull(cast(avg(b.score) as decimal(18,2)),0) 平均分 from Student a left join SCore b on a.S_id = b.S_id left join Course c on b.C_id = c.C_id group by a.S_id , a.Sname order by 平均分 desc
分析:
如果不用case..when,則查詢結果如第3個SQL。此SQL需解釋2點:1.max(case c.Cname when '語文' then b.score else null end) 語文, 2.ifnull(cast(avg(b.score) as decimal(18,2)),0) 平均分
第1點:首先,關於MYsql的case函數有這樣的介紹(詳細介紹見http://www.jb51.net/article/28680.htm):
CASE 具有兩種格式:
簡單 CASE 函數將某個表達式與一組簡單表達式進行比較以確定結果。
CASE 搜索函數計算一組布爾表達式以確定結果。
兩種格式都支持可選的 ELSE 參數。
該SQL的case函數屬於簡單 CASE 函數,如果表達式1 c.Cname與表達式2 '語文'比對結果為true時,返回結果 b.score,否則返回結果 null(這有點類似於三目運算符)。那么問題是,為什么前面要加個聚合函數 max呢?現在將該SQL簡化一下:
select a.S_id 學生編號 , a.Sname 學生姓名,
case c.Cname when '語文' then b.score else null end 語文,
case c.Cname when '數學' then b.score else null end 數學,
case c.Cname when '英語' then b.score else null end 英語
from Student a
left join SCore b on a.S_id = b.S_id
left join Course c on b.C_id = c.C_id
查詢結果(片段):
可以看出,由於沒有聚合函數,對於趙雷同學的三科成績有三條記錄,這樣的查詢結果顯然需要進一步優化——即利用max,結合group by a.S_id , a.Sname,就可以將上面的三條記錄合並為一條。
第2點:在ifnull(cast(avg(b.score) as decimal(18,2)),0) 平均分 中,cast是轉換格式函數,將avg(b.score)轉換成decimal(18,2)的格式(最多18位,小數點右邊最多2位);ifnull是判斷參數cast(avg(b.score) as decimal(18,2))的值是否為null,如果是,查詢結果顯示為0。
結果:
5.查詢學生平均成績及其名次
方法1
select t1.* , (select count(1) from( select m.S_id 學生編號 , m.Sname 學生姓名 , ifnull(cast(avg(score) as decimal(18,2)),0) 平均成績 from Student m left join score n on m.S_id = n.S_id group by m.S_id , m.Sname ) t2 where 平均成績 > t1.平均成績) + 1 as 名次 from( select m.S_id 學生編號, m.Sname 學生姓名 , ifnull(cast(avg(score) as decimal(18,2)),0) 平均成績 from Student m left join score n on m.S_id = n.S_id group by m.S_id , m.Sname ) t1 order by 名次
方法2
select t1.* , (select count(distinct t2.平均成績) from( select m.S_id 學生編號 , m.Sname 學生姓名 , ifnull(cast(avg(score) as decimal(18,2)),0) 平均成績 from Student m left join SCore n on m.S_id = n.S_id group by m.S_id , m.Sname ) t2 where t2.平均成績 >= t1.平均成績) as 名次 from( select m.S_id 學生編號 , m.Sname 學生姓名 , ifnull(cast(avg(score) as decimal(18,2)),0) 平均成績 from Student m left join SCore n on m.S_id = n.S_id group by m.S_id , m.Sname ) t1 order by 名次
分析(方法2):此SQL雖然較長,但其查詢原理同第1條、第3條SQL。關鍵在於理解“名次”是怎么來的。從結構上看,此SQL也是通過對同一結果查詢了2次而實現的(第1條SQL是對score表查詢了2次)。查詢t1.* ,select count(distinct t2.平均成績),條件是:t2.平均成績 >= t1.平均成績,其中,在查詢列select count(distinct t2.平均成績)時,會拿當前記錄行的t1的平均成績與t2中的平均成績進行比較,返回比當前記錄行中t1的平均成績大於或等於的條數——即當前記錄行中的平均成績的名次,如在結果(圖2)中的第一條記錄中,名次列中的1即是當前記錄行中的t1的平均成績(93.50)與t2中的平均成績比較符合條件“比93.50大於或等於”的條數。
結果:
圖2
6.查詢平均成績大於等於85的所有學生的學號、姓名和平均成績
select a.S_id , a.Sname , cast(avg(b.score) as decimal(18,2)) 平均分 from Student a , score b where a.S_id = b.S_id group by a.S_id , a.Sname having cast(avg(b.score) as decimal(18,2)) >= 85 order by 平均分 desc
注意:對於聚合函數的判斷用having而不用where。
結果:
7.查詢平均成績小於60分的同學的學生編號和學生姓名和平均成績
7.1查詢在score表存在成績的學生信息的SQL語句。
select a.S_id , a.Sname , cast(avg(b.score) as decimal(18,2)) avg_score from Student a , score b where a.S_id = b.S_id group by a.S_id, a.Sname having cast(avg(b.score) as decimal(18,2)) < 60 order by a.S_id
7.2查詢包括在score表中不存在成績記錄的學生信息的SQL語句。
select a.S_id, a.Sname , ifnull(cast(avg(b.score) as decimal(18,2)),0) avg_score from Student a left join score b on a.S_id = b.S_id group by a.S_id , a.Sname having ifnull(cast(avg(b.score) as decimal(18,2)),0) < 60 order by a.S_id
分析:1.要返回score表中的null記錄就需要左外連接left join;2.在having條件中,只有利用ifnull將null轉換為0,才能與具體的數(60)進行比較。
8. 查詢每門課程的平均成績,結果按平均成績降序排列,平均成績相同時,按課程編號升序排列
select m.C_id , m.Cname , cast(avg(n.score) as decimal(18,2)) 平均分 from Course m, SCore n where m.C_id = n.C_id group by m.C_id , m.Cname order by 平均分 desc, m.C_id asc
9.查詢所有同學的學生編號、學生姓名、選課總數、所有課程的總成績
9.1 查詢所有有成績的SQL
select a.s_id '學生編號', a.Sname '學生姓名', count(b.c_id) 選課總數, sum(score) '所有課程的總成績' from Student a , SCore b where a.s_id = b.s_id group by a.s_id,a.Sname order by sum(score) desc
分析:對於返回的選課總數、所有課程的總成績的依據條件均是a.s_id,a.Sname
9.2查詢所有(包括有成績和無成績)的SQL
select a.s_id as '學生編號', a.Sname as '學生姓名', count(b.c_id) as '選課總數', sum(score) as '所有課程的總成績' from Student a left join SCore b on a.s_id = b.s_id group by a.s_id,a.Sname order by sum(score) desc
分析:通過9.1與9.2的比較可以很典型地說明對於多表查詢有無left(right)join..on的區別,有了左外連接或右外連接,可以返回null記錄,而如果沒有(或join..on或inner..join)則不返回null記錄。
10.查詢不及格的課程
select Student.* , Course.Cname , SCore.C_id, SCore.score from Student, SCore , Course where Student.S_id = SCore.S_id and SCore.C_id = Course.C_id and SCore.score < 60 order by Student.S_id , SCore.C_id
11.查詢兩門及其以上不及格課程的同學的學號,姓名及其平均成績
11.1查詢在score表存在成績的學生信息的SQL語句。
select student.S_id, student.sname , cast(avg(score) as decimal(18,2)) 平均分 from student , SCore where student.S_id = SCore.S_id and student.S_id in (select S_id from SCore where score < 60 group by S_id having count(1) >= 2) group by student.S_id , student.sname order by 平均分 desc
11.2查詢包括在score表不存在成績記錄的學生信息的SQL語句。(注:此條SQL不符需求,題目是查詢至少2門不及格的同學的平均成績,這里卻是查詢平均成績小於60的信息。二者是不同的需求,比如某同學二科成績均為59,另一科100,那么他的平均分會高於60,這個同學信息就滿足前者而不滿足后者。)
select a.S_id, a.Sname , ifnull(cast(avg(b.score) as decimal(18,2)),0) 平均分 from Student a left join score b on a.S_id = b.S_id group by a.S_id , a.Sname having ifnull(cast(avg(b.score) as decimal(18,2)),0) < 60 order by 平均分 desc
12. 查詢在score表中不存在成績的學生信息的SQL語句/查詢沒有參加任何一門課程考試(缺考所有課程)的學生信息的SQL語句。
select a.s_id, a.Sname , ifnull(cast(avg(b.score) as decimal(18,2)),0) avg_score from Student a left join score b on a.s_id = b.s_id group by a.s_id , a.Sname having ifnull(cast(avg(b.score) as decimal(18,2)),0) = 0 order by a.s_id
分析:如果一個同學沒有參加任何一門課程的考試,那么其平均分肯定為0,所以查詢利用ifnull(cast(avg(b.score) as decimal(18,2)),0) = 0 作為查詢條件即可查出。
延伸:如果查詢缺考指定課程的同學記錄呢?
示例:查詢缺考課程編號為1的同學信息。
如果想以左外連接(left join)進行查詢,即在結果表中有s_id的記錄,而對應的c_id、score為null記錄,然后以此為條件進行篩選是行不通的。如:
select a.s_id,a.sname,c.c_id,cast(b.score as decimal(18,2)) 成績
from student a
left join score b on a.s_id=b.s_id
left join course c on c.c_id=b.c_id
where c.c_id=1 and c.c_id=null and cast(b.score as decimal(18,2)) =null
分析:這是因為此SQL即使去掉where條件(藍色SQL),所查詢的結果也沒有哪條記錄是有s_id,而對應的c_id、score為null的記錄。下面是藍色SQL的查詢結果(片段):
從上面的結果可以看出鄭竹同學缺考了課程為1(c_id=1)的考試,但是卻沒有查出來,查出來的只是王菊同學。這是為什么呢?這是因為左外連接(left join)返回的是左表的全部記錄以及與聯結字段所匹配的記錄:返回student表的所有記錄(8個同學),以及score b on a.s_id=b.s_id、course c on c.c_id=b.c_id所匹配的記錄,這兩個條件匹配了2條鄭竹同學的成績記錄。
因此,解決此查詢,需換另一個角度。再分析一下題目:查詢缺考課程編號為1的同學信息,如李某,首先在student表肯定有李某的信息,在score表缺少了李某對應的課程編號為1的成績記錄,但卻有所有參加了該課程考試的同學的相應記錄,那么查詢的邏輯就出來了:先查出在student表中的所有記錄,再在score表中查詢所有參加了課程編號為1的所有記錄中的同學編號(s_id),利用not in進行比對,即可查出結果。
select * from student where student.s_id not in( select st.s_id from score s,student st,course c where s.s_id=st.s_id and s.c_id=c.c_id and c.c_id=1 )
結果:
13.查詢課程名稱為"數學",且分數低於60的學生姓名和分數
select sname , score from Student , SCore , Course where SCore.S_id = Student.S_id and SCore.C_id = Course.C_id and Course.Cname = N'數學' and score < 60
14.檢索"01"課程分數小於60,按分數降序排列的學生信息
select student.* , score.C_id , score.score from student , score where student.S_id = score.S_id and score.score < 60 and score.C_id = '01' order by score.score desc
15.查詢課程編號為01且課程成績在80分以上的學生的學號和姓名
select Student.* , Course.Cname , SCore.C_id , SCore.score from Student, SCore , Course where Student.S_id = SCore.S_id and SCore.C_id = Course.C_id and SCore.C_id = '01' and SCore.score >= 80 order by Student.S_id , SCore.C_id
16.查詢選修"張三"老師所授課程的學生中,成績最高的學生信息及其成績
為了說明以下兩種情況,現在改變一下數據:“張三”老師所授的課程編號為2,現在將學號為2的學生的該課程成績由60改為99,這樣成績表中就有2條記錄。
16.1當最高分只有一個時
select Student.* , Course.Cname , SCore.C_id , SCore.score from Student, SCore , Course , Teacher where Student.S_id = SCore.S_id and SCore.C_id = Course.C_id and Course.T_id = Teacher.T_id and Teacher.Tname = N'張三' order by SCore.score desc limit 0,1
16.2當最高分出現多個時
select Student.* , Course.Cname , SCore.C_id , SCore.score from Student, SCore , Course , Teacher where Student.S_id = SCore.S_id and SCore.C_id = Course.C_id and Course.T_id = Teacher.T_id and Teacher.Tname = N'張三' and SCore.score = (select max(SCore.score) from SCore , Course , Teacher where SCore.C_id = Course.C_id and Course.T_id = Teacher.T_id and Teacher.Tname = N'張三')
分析:該SQL先查出張三老師所授的課程中所有同學及分數的信息,在這個結果中以分數作為限定條件——“其中分數最大的”進行篩選。
17.查詢s_id=1的學生的平均分
select a.S_id , a.Sname , cast(avg(b.score) as decimal(18,2)) avg_score from Student a , score b where a.S_id = b.S_id and a.S_id=1 group by a.S_id, a.Sname
18. 查詢任何一門課程成績在70分以上的學生姓名、課程名稱和分數
select Student.* , Course.Cname , SCore.C_id , SCore.score from Student, SCore , Course where Student.S_id = SCore.S_id and SCore.C_id = Course.C_id and SCore.score >= 70 order by Student.S_id , SCore.C_id
19.查詢"01"課程比"02"課程成績高的學生的信息及課程分數
19.1查詢同時存在"01"課程和"02"課程的情況
select a.* , b.score '課程"01"的分數',c.score '課程"02"的分數' from Student a , SCore b , SCore c where a.S_id = b.S_id and a.S_id = c.S_id and b.C_id = '01' and c.C_id = '02' and b.score > c.score
19.2查詢同時存在"01"課程和"02"課程的情況和存在"01"課程但可能不存在"02"課程的情況(不存在時顯示為null)
select a.* , b.score '課程"01"的分數',c.score '課程"02"的分數' from Student a left join SCore b on a.S_id = b.S_id and b.C_id = '01' left join SCore c on a.S_id = c.S_id and c.C_id = '02' where b.score > ifnull(c.score,0)
分析:由於需要比較兩門課程的分數,所以需要查詢兩次score表:score b,score b。
注意:該SQL的條件中一定要用ifnull,否則當c.score為null時,b.score無法與之進行比較。
20. 查詢不同課程成績相同(成績相同的課程)的學生的學生編號、課程編號、學生成績
方法1
select m.* from SCore m ,(select C_id , score from SCore group by C_id , score having count(1) > 1) n where m.C_id= n.C_id and m.score = n.score order by m.C_id , m.score , m.S_id
分析:解決此查詢的關鍵在於查出成績相同的課程,SQL“select C_id , score from SCore group by C_id , score having count(1) > 1” 即是此目的,其中,group by C_id , score having count(1) > 1 表示在score中C_id 相同並且score相同的記錄大於1,此SQL返回score表中相同的課程出現重復的分數的C_id、score,結果如下:
由於上述查詢結果是根據C_id , score查詢的,因此不能正確返回相應的s_id,解決這一點,只需把此結果作為條件,查詢一下score表 m,以
m.C_id= n.C_id and m.score = n.score 作為聯結條件即可查出所需結果。
方法2(注:select 1可以換成select n.* ,表示查詢符合條件的所有記錄。)
select m.* from SCore m where exists ( select 1 from (select C_id , score from SCore group by C_id , score having count(1) > 1) n where m.C_id= n.C_id and m.score = n.score ) order by m.C_id , m.score , m.S_id
分析:比較方法1,此方法利用了exists這個關鍵字,EXISTS 是判斷是否存在,和in類似。但需注意的是,這里的exists並不能換成in——這很能典型地說明它們的區別。【那么它們有什么區別呢?
需說明的是,in和exists的區別不止一點,在這里所體現的是:
Exists是存在判斷,只要有其中一個存在就返回。
in是返回所有包含在in中的數據。
現在就結合當前的例子加以說明:
假設換成in ,則SQL為:
select m.* from SCore m where m.c_id in (
select 1 from (select C_id , score from SCore group by C_id , score having count(1) > 1) n
where m.C_id= n.C_id and m.score = n.score
)
order by m.C_id , m.score , m.S_id
查詢結果:
只是看以上的結果似乎還不能明白什么是“in是返回所有包含在in中的數據”,為了弄清楚這句話的含義,現在來看一下SQL..】待續..
結果: