提高sql水平,記錄怒刷50道經典sql題【包含建表sql、答案sql、想提升sql水準的力薦一看】

今年六月份離開學校了,隔一段時間對一些東西進行總結
我堅信,時間會見證努力的腳步
特此立文,刷網上比較熱門的50道sql,裏面的思路和sql均爲本人刷題路上的記錄
歡迎你們學習探討,有錯也歡迎你們指出
由於本人須要上班,一有空閒有腦力就會繼續作題補充,歡迎收藏或者關注,以便後續閱讀
1.學生表
Student(S#,Sname,Sage,Ssex) 
--S# 學生編號,Sname 學生姓名,Sage 出生年月,Ssex 學生性別
2.課程表 
Course(C#,Cname,T#) 
--C# --課程編號,Cname 課程名稱,T# 教師編號
3.教師表 
Teacher(T#,Tname)
 --T# 教師編號,Tname 教師姓名
4.成績表 
SC(S#,C#,score)
 S# 學生編號,C# 課程編號,score 分數**mysql

建立測試數據sql

學生表 Student數組

create table Student(SId varchar(10),Sname varchar(10),Sage datetime,Ssex varchar(10));
insert into Student values('01' , '趙雷' , '1990-01-01' , '男');
insert into Student values('02' , '錢電' , '1990-12-21' , '男');
insert into Student values('03' , '孫風' , '1990-12-20' , '男');
insert into Student values('04' , '李雲' , '1990-12-06' , '男');
insert into Student values('05' , '周梅' , '1991-12-01' , '女');
insert into Student values('06' , '吳蘭' , '1992-01-01' , '女');
insert into Student values('07' , '鄭竹' , '1989-01-01' , '女');
insert into Student values('09' , '張三' , '2017-12-20' , '女');
insert into Student values('10' , '李四' , '2017-12-25' , '女');
insert into Student values('11' , '李四' , '2012-06-06' , '女');
insert into Student values('12' , '趙六' , '2013-06-13' , '女');
insert into Student values('13' , '孫七' , '2014-06-01' , '女');

科目表 Course函數

create table Course(CId varchar(10),Cname nvarchar(10),TId varchar(10));
insert into Course values('01' , '語文' , '02');
insert into Course values('02' , '數學' , '01');
insert into Course values('03' , '英語' , '03');

教師表 Teacher學習

create table Teacher(TId varchar(10),Tname varchar(10));
insert into Teacher values('01' , '張三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');

成績表 SC測試

create table SC(SId varchar(10),CId varchar(10),score decimal(18,1));
insert into SC values('01' , '01' , 80);
insert into SC values('01' , '02' , 90);
insert into SC values('01' , '03' , 99);
insert into SC values('02' , '01' , 70);
insert into SC values('02' , '02' , 60);
insert into SC values('02' , '03' , 80);
insert into SC values('03' , '01' , 80);
insert into SC values('03' , '02' , 80);
insert into SC values('03' , '03' , 80);
insert into SC values('04' , '01' , 50);
insert into SC values('04' , '02' , 30);
insert into SC values('04' , '03' , 20);
insert into SC values('05' , '01' , 76);
insert into SC values('05' , '02' , 87);
insert into SC values('06' , '01' , 31);
insert into SC values('06' , '03' , 34);
insert into SC values('07' , '02' , 89);
insert into SC values('07' , '03' , 98);

50道練習題優化

1. 查詢" 01 "課程比" 02 "課程成績高的學生的信息及課程分數

     查詢同時存在" 01 "課程和" 02 "課程的狀況

     查詢存在" 01 "課程但可能不存在" 02 "課程的狀況(不存在時顯示爲 null )

     查詢不存在" 01 "課程但存在" 02 "課程的狀況

2. 查詢平均成績大於等於 60 分的同窗的學生編號和學生姓名和平均成績

3. 查詢在 SC 表存在成績的學生信息

4. 查詢全部同窗的學生編號、學生姓名、選課總數、全部課程的總成績(沒成績的顯示爲 null )

     查有成績的學生信息

5. 查詢「李」姓老師的數量 

6. 查詢學過「張三」老師授課的同窗的信息 

7. 查詢沒有學全全部課程的同窗的信息 

8. 查詢至少有一門課與學號爲" 01 "的同窗所學相同的同窗的信息 

9. 查詢和" 01 "號的同窗學習的課程徹底相同的其餘同窗的信息 

10. 查詢沒學過"張三"老師講授的任一門課程的學生姓名 

11. 查詢兩門及其以上不及格課程的同窗的學號,姓名及其平均成績 

12. 檢索" 01 "課程分數小於 60,按分數降序排列的學生信息

13. 按平均成績從高到低顯示全部學生的全部課程的成績以及平均成績

14. 查詢各科成績最高分、最低分和平均分:

    以以下形式顯示:課程 ID,課程 name,最高分,最低分,平均分,及格率,中等率,優良率,優秀率
    及格爲>=60,中等爲:70-80,優良爲:80-90,優秀爲:>=90
    要求輸出課程號和選修人數,查詢結果按人數降序排列,若人數相同,按課程號升序排列

15. 按各科成績進行排序,並顯示排名, Score 重複時保留名次空缺

16. 查詢學生的總成績,並進行排名,總分重複時保留名次空缺

17. 統計各科成績各分數段人數:課程編號,課程名稱,[100-85],[85-70],[70-60],[60-0] 及所佔百分比

18. 查詢各科成績前三名的記錄

19. 查詢每門課程被選修的學生數 

20. 查詢出只選修兩門課程的學生學號和姓名 

21. 查詢男生、女生人數

22. 查詢名字中含有「風」字的學生信息

23. 查詢同名同性學生名單,並統計同名人數

24. 查詢 1990 年出生的學生名單

25. 查詢每門課程的平均成績,結果按平均成績降序排列,平均成績相同時,按課程編號升序排列

26. 查詢平均成績大於等於 85 的全部學生的學號、姓名和平均成績 

27. 查詢課程名稱爲「數學」,且分數低於 60 的學生姓名和分數 

28. 查詢全部學生的課程及分數狀況(存在學生沒成績,沒選課的狀況)

29. 查詢任何一門課程成績在 70 分以上的姓名、課程名稱和分數

30. 查詢不及格的課程

31. 查詢課程編號爲 01 且課程成績在 80 分以上的學生的學號和姓名

32. 求每門課程的學生人數 

33. 成績不重複,查詢選修「張三」老師所授課程的學生中,成績最高的學生信息及其成績

34. 成績有重複的狀況下,查詢選修「張三」老師所授課程的學生中,成績最高的學生信息及其成績

35. 查詢不一樣課程成績相同的學生的學生編號、課程編號、學生成績 

36. 查詢每門功成績最好的前兩名

37. 統計每門課程的學生選修人數(超過 5 人的課程才統計)。

38. 檢索至少選修兩門課程的學生學號 

39. 查詢選修了所有課程的學生信息

40. 查詢各學生的年齡,只按年份來算 

41. 按照出生日期來算,當前月日 < 出生年月的月日則,年齡減一

42. 查詢本週過生日的學生

43. 查詢下週過生日的學生

44. 查詢本月過生日的學生

45. 查詢下月過生日的學生

怒刷結果以下【僅供參考,一家之言,有更好的方式歡迎溝通交流】:編碼

1. 查詢" 01 "課程比" 02 "課程成績高的學生的信息及課程分數

    思路:
    先查詢01和02的sid和對應的分數組成兩張中間表,利用left join連表
    where加條件篩選,最後再利用sid和學生表join拿出全部信息,sql以下:
    select t3.*,st.* from
    (select t1.sid,s1,s2 from 
    (select Sid,score s1 from sc where Cid = '01')t1 
    left join
    (select Sid,score s2 from sc where Cid = '02')t2
    on t1.sid = t2.sid
    where t1.s1 > t2.s2 )t3
    left join student st on st.sid = t3.sid

     查詢同時存在" 01 "課程和" 02 "課程的狀況
     
     思路:
     是上面的題目的子思路吧,只要將01課程和02課程的信息所有篩出來
     而後令他們等於同一我的就好了,知足就select出來
    select * from
    (select * from sc where cid = '01')t1 
    join (select * from sc where cid = '02')t2 
    on t1.sid = t2.sid
    
    查詢存在" 01 "課程但可能不存在" 02 "課程的狀況(不存在時顯示爲 null )
    思路:
    和上面惟一不一樣的僅僅是結果集的包含關係。
    上面的題目是一對一,join保證出A-B的關係
    如今的題目是改爲A-B + A-NULL的關係
    只須要講join 改爲left join便可
     select * from
    (select * from sc where cid = '01')t1 
    left join (select * from sc where cid = '02')t2 
    on t1.sid = t2.sid
    
    查詢不存在" 01 "課程但存在" 02 "課程的狀況
    思路:
    查詢出選了02課程下的選課信息,在其中篩選掉沒選01課程的信息便可
    select t1.* from sc t1 
    where t1.cid = '02'
    and
    not exists (select 1 from sc where cid = '01' and sid = t1.sid)

2. 查詢平均成績大於等於 60 分的同窗的學生編號和學生姓名和平均成績code

思路:下方的寫法也能夠把where那一段條件放到group by 後面的 having裏對分組結果進行過濾
select  ROUND(t1.score,2) score ,t1.sid,t2.Sname from
    (select avg(score) score , sid  from sc GROUP BY sid)t1
    left join student t2 on t1.sid = t2.sid
    where t1.score >'60'

3. 查詢在 SC 表存在成績的學生信息排序

思路:題意不明,什麼叫SC表存在成績的學生信息..這有好多種可能
這裏按照最可能的思路解題(即出如今表裏的都看成有成績的學生)
查出sc表全部的學生的sid,而後left join上學生表便可
select t2.* from 
    (select distinct sid from sc )t1
    left join student t2 on t1.sid = t2.sid

4.查詢全部同窗的學生編號、學生姓名、選課總數、全部課程的總成績(沒成績的顯示爲 null )

思路:學生表拿出學生編號+學生姓名,左鏈接sc表,而後分組便可
select t1.sid,t1.sname,count(t2.cid),sum(t2.score) from student t1  
left join sc t2 on t1.sid = t2.sid
GROUP BY t1.sid,t1.sname

5.查詢「李」姓老師的數量

思路:簡單,teacher表裏進行模糊查詢便可
select count(1) from teacher where tname like '%李%'

6. 查詢學過「張三」老師授課的同窗的信息

思路:不難,理清楚幾張表的邏輯關係就能夠寫出來..直接想思路反而難。大概作個思路總結,即主體是查詢同窗的信息,即select * from student 而後條件是他的sid在張三老師的授課課程裏。那麼問題轉變,查出張三老師教的課程對應的分數表裏的數據,包含了sid的,取出來和前面的學生表對應上便可
select * from student where exists 
    (select 1 from
    (select sid from course t1
    left join sc t2 on t1.cid = t2.cid
    where t1.tid = (select tid from teacher where tname = '張三'))t
    where t.sid = student.sid)

7. 查詢沒有學全全部課程的同窗的信息

思路:問題即課程表sc裏對學生編碼sid進行分組,若是成績總條數和課程表總課程數對應上,那麼左鏈接上學生表便可。
select t2.* from
    (select sid,count(1) count from sc group by sid )t1 
    left join student t2 on t1.sid = t2.sid
    where t1.count = (select count(1) from course);

8. 查詢至少有一門課與學號爲" 01 "的同窗所學相同的同窗的信息

思路:1.查出01同窗全部課程;2.查出除01同窗外的同窗所學課程至少一門和1查出來的相等;
3.整理出對應的惟一sid;4.連表查詢或者子查詢查出相關學生信息便可。
select t2.* from 
    (select distinct sid from sc where sc.sid != '01' and exists (select 1 from sc where sid = '01' and cid = sc.cid ))t1
    left join student t2 on t1.sid = t2.sid

9. 查詢和" 01 "號的同窗學習的課程徹底相同的其餘同窗的信息

思路:和上面相比,就是篩選sid的條件有更苛刻的限制。只須要對上面exists部分進行改造
嗯.........我收回上面這個這麼簡單的思路,事實上我寫這一條用了30分鐘【吐血..】
SELECT * FROM student 
WHERE
    sid IN (
    SELECT
        z.SId 
    FROM
        ( SELECT GROUP_CONCAT( CId ) x, SId FROM sc GROUP BY SId ) z 
    WHERE
        z.x = ( SELECT GROUP_CONCAT( CId ) FROM sc WHERE SId = '01' ) 
    AND SId != '01' 
    )
優化版本
SELECT * FROM student 
WHERE
    exists  (
    SELECT
        1 
    FROM
        ( SELECT GROUP_CONCAT( CId ) x, SId FROM sc GROUP BY SId ) z 
    WHERE
        z.x = ( SELECT GROUP_CONCAT( CId ) FROM sc WHERE SId = '01' ) 
    AND SId != '01' and sid = student .sid
    )
[這裏注意,GROUP_CONCAT是mysql的函數寫法,Oracle對應的是wm_CONCAT函數]

10. 查詢沒學過"張三"老師講授的任一門課程的學生姓名

思路:和第9題對比起來簡直小巫見大巫……嗯,又年輕了,仍是想了有十分鐘左右.思路即先查出張三這位老師教學的課程集合,而後找出選了他的課程的學生(即sc表有對應記錄),再排除掉這些學生便可。sql以下:
子查詢寫法,優化思路應該把in改爲exists,照着邏輯寫着寫着這寫出來的,下面寫一個連表的sql

select sname from student where sid not in(

select distinct sid from sc where cid in 
    (select cid from course where exists(select 1 from teacher where tname = '張三' and tid = course.tid))
    );
select sname from student where not exists(
    select 1 from teacher t1 
    left join course t2 on t1.tid = t2.tid
    left join sc t3 on t3.cid = t2.cid
    where t1.tname = '張三' and student.sid = sid )

11. 查詢兩門及其以上不及格課程的同窗的學號,姓名及其平均成績

思路:難度通常,考察分組和函數使用即連表。
select sc.sid,round(avg(sc.score),2),student.sname from sc 
    left join student on student.sid = sc.sid 
    where sc.score <60 
    group by sc.sid 
    having sum(sc.sid)>2

12. 檢索" 01 "課程分數小於 60,按分數降序排列的學生信息

思路:基礎難度,考察排序,以下
select student.* from sc 
    left join student on student.sid = sc.sid
    where score < 60 and cid = '01' order by score desc

13. 按平均成績從高到低顯示全部學生的全部課程的成績以及平均成績

思路:兩個點,主體是全部學生的全部課程的成績和平均成績,另外一個是根據學平生均成績排好序。那麼問題即,對學生先分組查出平均成績的高低狀況,順便把平均成績也能夠統計出來了,而後排好序left join同樣的sc成績表便可。固然,這裏join也能夠,結果同樣。
select t1.*,t2.score
    from
    (select 
    sid,
    round(avg(score),2) avgScore
    from sc t1 
    GROUP BY sid 
     )t1
    left join sc t2 on t1.sid = t2.sid
    order by t1.avgScore desc

14. 查詢各科成績最高分、最低分和平均分:

思路:這個真的也不難..根據各科id分組,而後用聚合函數便可.直接上sql
select
        cid, max(score),  min(score),  avg(score)
    from  sc
    GROUP BY cid

好吧,審題不當,這裏上正確的第14題


14:查詢各科成績最高分、最低分和平均分:
以以下形式顯示:課程 ID,課程 name,最高分,最低分,平均分,及格率,中等率,優良率,優秀率
及格爲>=60,中等爲:70-80,優良爲:80-90,優秀爲:>=90
要求輸出課程號和選修人數,查詢結果按人數降序排列,若人數相同,按課程號升序排列

思路:比上面難度增長的其實就是多了case結構塊,實話說,在沒參與工做以前,我這條sql是寫不出的。在一方面見證了我的軟技能的些許進步吧.跑題了,這裏總結一下case的語法》
1.case XX when A then XXXX when B then YYYY ...else zzz  end 
2.case when XX+條件 when XXXX then yyyy ...else ZZZ end
在行列轉換上頗有幫助.
select 
        sc.CId ,
        max(sc.score)as maxScore,
        min(sc.score)as mimScore,
        avg(sc.score)as avgScore,
        count(1)as 選修人數,
        sum(case when sc.score>=60 then 1 else 0 end )/count(*)as 及格率,
        sum(case when sc.score>=70 and sc.score<80 then 1 else 0 end )/count(*)as 中等率,
        sum(case when sc.score>=80 and sc.score<90 then 1 else 0 end )/count(*)as 優良率,
        sum(case when sc.score>=90 then 1 else 0 end )/count(*)as 優秀率 
    from sc
    GROUP BY sc.CId
    ORDER BY count(1)DESC, sc.CId ASC

15按各科成績進行排序,並顯示排名, Score 重複時保留名次空缺

思路:參考了網上資料,自關聯,根據比分數大的總數來肯定排名,sql以下:
select a.cid, a.sid, a.score, count(b.score)+1 as rank
    from sc as a 
    left join sc as b 
    on a.score<b.score and a.cid = b.cid
    group by a.cid, a.sid,a.score
    order by a.cid, rank ASC;

16.查詢學生的總成績,並進行排名,總分重複時保留名次空缺

思路:一樣的,分組求和統計,考察變量的使用。
解法1 用變量
    set @temp=0;
    select q.sid, sum, @temp := @temp +1 as rank from(
    select sc.sid, sum(sc.score) as sum from sc
    group by sc.sid
    order by sum desc)q;
    解法2 強暴寫法
    select t1.* ,count(t2.sid)+1 rank
    from
    (select 
        sid,
        sum(score) sum 
    from sc 
    GROUP BY sid 
    order by sum desc)t1
    left join 
    (select 
        sid,
        sum(score) sum 
    from sc 
    GROUP BY sid 
    order by sum desc)t2
    on t1.sum < t2.sum and t1.sid != t2.sid
    GROUP BY t1.sid
    order by rank asc;

17. 統計各科成績各分數段人數:課程編號,課程名稱,[100-85],[85-70],[70-60],[60-0] 及所佔百分比

思路:分數表根據cid課程編碼進行分組,而後對知足條件的進行篩選運算就行了,就不轉成百分比了,要轉的話可使用round(xx,2)先對的出來的小數進行保留2位小數的運算,而後乘以100拼接上%號就行了.CONCAT(a,b).
select 
        sc.cid ,
        course.cname,
        sum(case when sc.score >=85 and sc.score <= 100 then 1 else 0 end)/count(*) as "[100-85]",
        一樣的結構快..
    from sc
    left join course on course.cid = sc.cid
    GROUP BY sc.cid

查詢各科成績前三名的記錄

思路:未完待續
相關文章
相關標籤/搜索