轉:sql 經典50題--多是你見過的最全解析

題記:從知乎上看到的一篇文章,恰好最近工做中發現遇到的題目與這個幾乎同樣,可能就是從這裏來的吧。^_^sql

裏面的答案沒有細看,SQL求解重在思路,不少時候同一種結果可能有多種寫法,好比題中的各科成績取前三名,我是在看到這篇文章想到的另外一種寫法,不過題中的寫法也能夠作爲參考學習。以此記錄。函數

如下轉自:https://zhuanlan.zhihu.com/p/72223558學習

這篇文寫了挺久了,有一位細心的小夥伴滑稽發現了一個錯誤,很感謝有人認真的在看,我從新修改了一下! 測試

好久以前我寫過一篇學習sql的基本操做,可是本身好像在sql上面有點停滯了。最近有和一位朋友聊過如何學習sql,朋友給個人回覆是多寫代碼。我深覺得然因而就找到了這個經典的50題。spa

雖然這個經典50題網上有不少人分享過,可是,只有本身實際操做過之後才能真正的理解其中的坑,也纔可以加深對知識的理解吧。我但願本身可以作一套很詳細的解析。.net

解析包含:1解題思路,2相關知識,3 實際語句實現;3d

申明:我只是一個初學者,還有不少不懂的地方,因此若是您發現了錯誤,但願能在下邊提出來,萬分感謝!code

若是你以爲我很走心,也歡迎點贊!orm

首先建表,主要有四個表,學生表(Student),課程表(Course),教師表(Teacher),以及成績表(SC)blog

在分別介紹一下每一個表的字段 學生表(Student )有四個字段 sid--學生id,sname--學生姓名,sage--學生年齡,ssex--學生性別

課程表(Course)有三個字段,cid--課程id,cname--課程名,tid--教師id

教師表(Teacher)有兩個字段,tid--教師id,tname--教師姓名

成績表(SC)有三個字段,sid--學生id,cid--課程id,score--成績

咱們用腦圖的方式繪製出來,以下所示

學生表結構

課程表結構

教師表結構

成績表結構

各個表之間的關係以下,Student和SC表經過學生id(sid)來鏈接,Course和Teacher表經過教師id(tid)鏈接,SC和Course表經過課程id(cid)來鏈接。

create table Student(sid varchar(10),sname varchar(10),sage datetime,ssex nvarchar(10));
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' , '');
create table Course(cid varchar(10),cname varchar(10),tid varchar(10));
insert into Course values('01' , '語文' , '02');
insert into Course values('02' , '數學' , '01');
insert into Course values('03' , '英語' , '03');
create table Teacher(tid varchar(10),tname varchar(10));
insert into Teacher values('01' , '張三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');
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);

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

解題思路:要查詢的是兩個課程的成績,並且還要顯示學生的信息。因此須要用到兩張表,SC,Student這兩張表。

問題拆分:(1) 怎麼查找兩個課程的成績呢? (2) 如何把課程表和學員信息錶鏈接起來呢?

那麼用到哪些知識呢?

(1) 子查詢 (2) join

語句實現:先找到兩門課的成績

-- 課程1的成績
SELECT sid ,score AS class1 FROM sc WHERE sc.cid = '01';
-- 課程2的成績
SELECT sid,score AS class2 FROM sc WHERE sc.cid = '02';

class1 查詢結果

class2 查詢結果

兩個子查詢結束了之後,下一步就是使用join把這兩個查詢的結果鏈接起來

SELECT * FROM Student RIGHT JOIN 
(SELECT t1.sid,class1,class2 
FROM 
-- 這個是要查詢兩個表的成績,即課程1大於課程2
(SELECT sid ,score AS class1 FROM sc WHERE sc.cid = '01') AS t1,
(SELECT sid,score AS class2 FROM sc WHERE sc.cid = '02') AS t2
WHERE t1.sid = t2.sid AND t1.class1 > t2.class2) r -- 這裏是固定的,就是子查詢的一個別名的設置
-- 這一步就是把兩個表按照sid聯結起來
ON Student.sid = r.sid;

最終查詢結果

好的,,第一題就解決了

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

思路:首先要查詢全部的學生的選課狀況,而後找到選擇課程1和選擇課程2的學生

解決方法: 使用join

選擇兩個分別選擇課程1和課程2的學員表,而後把這兩個表join起來

SELECT *
 FROM(SELECT *
 FROM sc
 WHERE sc.cid = '01' ) t1 LEFT JOIN 
 (SELECT *
 FROM sc WHERE sc.cid = '02') t2
 ON t1.sid = t2.sid ;

輸出結果

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

思路:要求同時選擇了01和02課程的狀況,則須要使用where連接起來,這一題和上一題相比就是多了一個去除選了01可是沒有選擇02課程的這一部分

知識點:子查詢,where語句

 SELECT *
 FROM (SELECT *
 FROM sc WHERE sc.cid = '01') AS t1,
 (SELECT * FROM sc WHERE sc.cid = '02') AS t2
 WHERE t1.sid = t2.sid;

執行結果

1.3 查詢選擇了02課程但沒有01課程的狀況

思路,要求首先獲得選擇了02課程的學員,而後剔除掉選擇01課程的學員

知識點:子查詢,NOT IN

SELECT *
 FROM sc
 WHERE sc.sid NOT IN (SELECT sid FROM sc WHERE sc.cid = '01')
 AND sc.cid = '02';

寫好了這個語句之後我以爲本身寫的不夠清楚,所以我使用了別名

看一下結果:

 SELECT *
 FROM sc
 WHERE sc.sid NOT IN 
 (SELECT sid FROM sc WHERE sc.cid = '01')
 AND sc.cid = '02';

執行結果

小結:上面幾個題基本能夠說是同一類型,要注意子查詢的使用,以及join和where的區別

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

思路:看到這個題的時候第一想法是要使用group by,和having,又由於成績表(sc)沒有學生姓名,因此還要使用join,固然avg確定是須要的

考察知識:group by,join

SELECT s.sid,sname,AVG(sc.score)
FROM student AS s INNER JOIN sc
ON s.sid = sc.sid 
GROUP BY sc.sid
HAVING AVG(sc.score) >= 60;

執行結果

還有一種解法,使用子查詢

SELECT r.*, s.sname FROM
(SELECT sid,AVG(score) FROM sc
GROUP BY sid 
HAVING AVG(score) > 60) r
LEFT JOIN student AS s ON s.sid = r.sid;

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

思路:這題比較簡單,就是兩張表經過id鏈接,而後求單獨值

知識點:Distinct

SELECT DISTINCT student.*
FROM sc,student
WHERE student.sid = sc.sid ;

執行結果

4.查詢全部同窗的學生編號、學生姓名、選課總數、全部課程的成績總和

解題思路:首先用到兩張表格,而後要求選課總數,和成績總和,那麼就須要用到groupby 和sum以及count

知識點:gruop by, sum,join,count

SELECT s.sid,s.sname,COUNT(sc.cid),SUM(score)
FROM student AS s INNER JOIN sc 
ON s.sid = sc.sid
GROUP BY sc.sid;

執行結果

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

解題思路:這題考的是通配符的查詢,好比使用like和%

知識點:count,like,%

SELECT COUNT(*)
FROM teacher
WHERE tname LIKE ('李%');

執行結果

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

解題思路:要查找張三老師的授課科目,而後經過科目和score錶鏈接,而後再和學生錶鏈接

知識點:就是多重鏈接 inner join

SELECT s.*
FROM student AS s INNER JOIN sc ON s.sid = sc.sid
INNER JOIN  course  AS c ON sc.cid = c.cid
INNER JOIN teacher AS t ON t.tid = c.tid
WHERE t.tname = '張三';

執行結果

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

解題思路:首先利用子查詢查詢course表查詢獲得共有幾門課,按照groupby的方式求課程數加上having小於查詢出來的課程的全部學員信息就好

知識點:子查詢,groupby,having,join

SELECT s.*,COUNT(cid)
FROM sc RIGHT JOIN student AS s 
ON sc.sid = s.sid
GROUP BY sc.sid
HAVING COUNT(cid) < (SELECT COUNT(DISTINCT cid)
FROM course);

執行結果

網友的有一種比較好的作法,是使用not in

SELECT s.*
FROM student AS s
WHERE s.sid NOT IN (SELECT sc.sid
FROM sc 
GROUP BY sc.sid
HAVING COUNT(cid) >= 3);-- 這一句也可使用子查詢

執行結果

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

解題思路:首先要查詢出01同窗所學的課程,而後使用cid in 01同窗的課程,並排除sid=01的同窗

SELECT s.*
FROM sc INNER JOIN student AS s
ON sc.sid = s.sid
WHERE sc.cid IN (SELECT cid
FROM sc
WHERE sid = '01') AND sc.sid != '01'
GROUP BY sc.sid;

執行結果

我這裏使用了groupby語句來求取單個學員id,也可使用distinct關鍵字來作區分

SELECT DISTINCT s.*
FROM sc INNER JOIN student AS s
ON sc.sid = s.sid
WHERE sc.cid IN (SELECT cid
FROM sc
WHERE sid = '01') AND sc.sid != '01';

執行結果

這裏就有一個知識點就是group by有去除重複值的功能,這個其實不難理解,由於group by就是按照單個組分類,能夠理解爲按照同一類進行切分

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

解題思路:首先感受真題很難,想法是在student表的sid在sc的id中,而這個id又和cid相關聯,而後再查詢結束時,還要排除id=01的狀況。反正很複雜。。。

知識點:n重子查詢,in的使用

  SELECT 
    * 
  FROM
    student 
  WHERE sid IN 
    (SELECT 
      sid 
    FROM
      (SELECT 
        * 
      FROM
        sc AS a 
      WHERE cid IN 
        (SELECT 
          cid 
        FROM
          sc 
        WHERE sid = 01)) b 
    GROUP BY sid 
    HAVING COUNT(cid) = 
      (SELECT 
        COUNT(cid) 
      FROM
        sc c 
      WHERE sid = 01)) 
    AND sid != 01 ;  SELECT 
    * 
  FROM
    student 
  WHERE sid IN 
    (SELECT 
      sid 
    FROM
      (SELECT 
        * 
      FROM
        sc AS a 
      WHERE cid IN 
        (SELECT 
          cid 
        FROM
          sc 
        WHERE sid = 01)) b 
    GROUP BY sid 
    HAVING COUNT(cid) = 
      (SELECT 
        COUNT(cid) 
      FROM
        sc c 
      WHERE sid = 01)) 
    AND sid != 01 ;

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

解題思路:1先要查詢出張三老師的教授課程,2而後利用not in 來找到學生的id

知識點:子查詢,not in,多重join

SELECT sname FROM student 
WHERE sid NOT IN (
SELECT sid FROM sc 
LEFT JOIN course ON sc.cid=course.cid
LEFT JOIN teacher ON course.tid=teacher.tid 
WHERE tname='張三' )

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

解題思路:題中提到了兩門及以上的不及格課程,所以考慮使用Groupby 和 having,題中還提到學生姓名以及平均成績,考慮到這裏還要求出平均成績,可是平均成績說明不是很清楚,

知識點:Group by ,having,以及子查詢在from子句中的使用

SELECT sname,s.sid,AVG(sc1.score) AS avg_score
FROM (SELECT * FROM sc WHERE score < 60)sc1,
student AS s 
WHERE sc1.sid=s.sid
GROUP BY sc1.sid
HAVING COUNT(sc1.cid)>=2;

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

解題思路:要求01課程小於60的學員信息,而後按照降序排列學員信息

知識點:就是很簡單的order by

SELECT s.*
FROM sc,student AS s
WHERE cid = '01' AND score <60
AND sc.sid = s.sid
ORDER BY score DESC;

固然還有其餘解法,好比使用join,這裏就很少寫了

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

解題思路:要按照平均成績降序排列,則須要使用group by,以及要顯示全部的課程信息,那麼就須要使用join來實現了

知識點:group by ,子查詢,join

SELECT 
  s.*,
  avg_score 
FROM
  sc AS s 
  LEFT JOIN 
    (SELECT 
      sc.sid,AVG(sc.score) AS avg_score 
    FROM
      sc 
    GROUP BY sc.sid 
    ORDER BY avg_score DESC) r 
    ON s.sid = r.sid ;

能夠發現這裏的作法,發現並無按照我想要的平均成績的從高到低來排序,因此就去排查一下緣由,發現這個錯誤是執行順序的關係。

正確作法

SELECT 
  s.*,
  avg_score 
FROM
  sc AS s 
  LEFT JOIN 
    (SELECT 
      sc.sid ,AVG(sc.score) AS avg_score 
    FROM
      sc 
    GROUP BY sc.sid 
   ) r 
    ON s.sid = r.sid 
 ORDER BY avg_score DESC ;

這樣就能夠了,所以啊,sql執行順序仍是要牢記的

14.查詢各科成績最高分、最低分和平均分,以以下形式顯示:

以以下形式顯示:課程 ID,課程 name,最高分,最低分,平均分,及格率,中等率,

優良率,優秀率

及格爲>=60,中等爲:70-80,優良爲:80-90,優秀爲:>=90

要求輸出課程號和選修人數,查詢結果按人數降序排列,若人數相同,按課程號升序

排列

解題思路:這裏要把最高分,最低分,平均分,等多個維度,那麼就須要使用case when 語句了還有就是函數的使用

知識點:case when 別名的使用,以及函數的使用,和函數的嵌套使用,題目後邊還考到了多列排列的使用,先按照desc,後按照asc排列

SELECT 
  cid AS 課程ID,
  COUNT(sid) AS 課程人數,
  MAX(score) AS 最高分,
  MIN(score) AS 最低分,
  AVG(score) AS 平均分,
  SUM(及格) / COUNT(sid) AS 及格率,
  SUM(中等) / COUNT(sid) AS 中等率,
  SUM(優良) / COUNT(sid) AS 優良率,
  SUM(優秀) / COUNT(sid) AS 優秀率 
FROM
  (SELECT 
    *,
    CASE
      WHEN score >= 60 
      THEN 1 
      ELSE 0 
    END AS 及格,
    CASE
      WHEN score >= 70 
      AND score < 80 
      THEN 1 
      ELSE 0 
    END AS 中等,
    CASE
      WHEN score >= 80 
      AND score < 90 
      THEN 1 
      ELSE 0 
    END AS 優良,
    CASE
      WHEN score >= 90 
      THEN 1 
      ELSE 0 
    END AS 優秀 
  FROM
    sc) a 
GROUP BY cid 
ORDER BY COUNT(sid) DESC,
  cid ;

特別長,可是知識很簡單,就是case when語句的使用

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

解題思路:兩張成績錶鏈接,而後按照成績大小作一個排名

知識點:排序相關知識

SELECT 
  a.*,
  COUNT(a.score) AS 排名 
FROM
  sc AS a 
  LEFT JOIN sc AS b 
    ON a.cid = b.cid 
    AND a.score < b.score 
GROUP BY a.cid,
  a.sid 
ORDER BY a.cid,
  排名 ;

15.1 按各科成績進行行排序,並顯示排名, Score 重複時合併名次

解題思路:相比於上一題這裏多了一個合併名次的事情,所謂名次合併,意思是兩我的的成績一致按照一個名次來,好比兩個成績同樣的排名第一,下一個排名第二的就當作第三名

SELECT 
  a.*,
  COUNT(b.score)+1 AS 排名 
FROM
  sc AS a 
  LEFT JOIN sc AS b 
    ON a.cid = b.cid 
    AND a.score < b.score 
GROUP BY a.cid,
  a.sid 
ORDER BY a.cid,
  排名 ;

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

解題思路:和15題差很少,不過我要引入一個用戶變量

知識點:用戶變量的使用

SELECT 
  a.*,
  @rank := @rank + 1 AS rank 
FROM
  (SELECT 
    sid,
    SUM(score) 
  FROM
    sc 
  GROUP BY sid 
  ORDER BY SUM(score) DESC) a,
  (SELECT 
    @rank := 0) b ;

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

解題思路:這一題,不熟練,參考了前輩們的作法

知識點:用戶變量,子查詢

SELECT 
  a.*,
  CASE
    WHEN @fscore = a.sumscore 
    THEN @rank 
    WHEN @fscore := a.sumscore 
    THEN @rank := @rank + 1 
  END AS 排名 
FROM
  (SELECT 
    sc.sid,
    SUM(score) AS sumscore 
  FROM
    sc 
  GROUP BY sid 
  ORDER BY SUM(score) DESC) AS a,
  (SELECT 
    @rank := 0,
    @fscore := NULL) AS t ;

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

解題思路:這個題和以前的那一題很像,都是使用case when 語句,而後加一個join

知識點:join case when,以及

SELECT 
  sc.cid AS 課程編號,
  cname AS 課程名稱,
  SUM(
    CASE
      WHEN score >= 0 
      AND score <= 60 
      THEN 1 
      ELSE 0 
    END
  ) AS '[60-0]',
  SUM(
    CASE
      WHEN score >= 0 
      AND score <= 60 
      THEN 1 
      ELSE 0 
    END
  ) / COUNT(sid) AS '[60-0]百分比',
  SUM(
    CASE
      WHEN score >= 60 
      AND score <= 70 
      THEN 1 
      ELSE 0 
    END
  ) AS '[70-60]',
  SUM(
    CASE
      WHEN score >= 60 
      AND score <= 70 
      THEN 1 
      ELSE 0 
    END
  ) / COUNT(sid) AS '[70-60]百分比',
  SUM(
    CASE
      WHEN score >= 70 
      AND score <= 85 
      THEN 1 
      ELSE 0 
    END
  ) AS '[85-70]',
  SUM(
    CASE
      WHEN score >= 70 
      AND score <= 85 
      THEN 1 
      ELSE 0 
    END
  ) / COUNT(sid) AS '[85-70]百分比',
  SUM(
    CASE
      WHEN score >= 85 
      AND score <= 100 
      THEN 1 
      ELSE 0 
    END
  ) AS '[100-85]',
  SUM(
    CASE
      WHEN score >= 85 
      AND score <= 100 
      THEN 1 
      ELSE 0 
    END
  ) / COUNT(sid) AS '[100-85]百分比' 
FROM
  sc 
  JOIN course 
    ON sc.cid = course.cid 
GROUP BY sc.cid,
  cname ;

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

解題思路:按照課程id和學生id來分組,而後合併兩個sc表,找到第一個表的成績比第二個表成績低的的值,而後排序

知識點: join groupby以及having的使用,固然還有order by的使用

SELECT a.*,COUNT(b.score) +1 AS ranking
FROM SC AS a LEFT JOIN SC AS b 
ON a.cid = b.cid AND a.score<b.score
GROUP BY a.cid,a.sid
HAVING ranking <= 3
ORDER BY a.cid,ranking;

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

解題思路:就是很簡單的按照cid分組,而後計算每一個組裏面的學生人數,因爲前面幾題都是想了好久,遇到這題,我居然不敢相信這麼簡單

知識點:groupby

SELECT cid,COUNT(sid) AS num
FROM  sc 
GROUP BY cid;

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

解題思路:查詢選擇了兩門課的學生,而後鏈接student表,還算是比較基礎的題

知識點:groupby having join

SELECT sc.sid,sname
FROM sc INNER JOIN student
ON sc.sid = student.sid
GROUP BY sc.sid
HAVING COUNT(sc.cid) = 2;

21. 查詢男生、女生人數

解題思路:男女生人數這個毫無疑問考的是groupby 還有就是分組之後的count,或者可使用case when

知識點:groupby,或者 case when

SELECT ssex,COUNT(ssex) as 
FROM student
GROUP BY ssex;

SELECT SUM(CASE WHEN ssex='' THEN 1 ELSE 0 END)AS 男生人數,
       SUM(CASE WHEN ssex='' THEN 1 ELSE 0 END) AS 女生人數
FROM student;

固然這裏不完美的,可使用count(sid)-男生人數,有不少的方式。

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

解題思路:很簡單的一題,就是考察通配符,而考察點是是否有風

知識點:通配符%

SELECT *
FROM student
WHERE sname LIKE '%風%';

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

解題思路:同名同性,咱們須要的是count(sname)>1,還要作一步篩選。這裏有一個小坑,開始的時候我覺得是同名同姓,中國文化博大精深啊

知識點:having,groupby

SELECT sname,COUNT(sname) AS 同名人數
FROM student
GROUP BY sname
HAVING 同名人數>1;

因爲這個數據表中沒有同名的人數,因此顯示結果如上。

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

解題思路:很簡單的題,可是我仍是作錯了

知識點:通配符

SELECT sname
FROM student
WHERE sage LIKE '1990%';

本覺得能夠很快的執行併產生結果,可是沒想到報錯了,看一下報錯信息,睡是datetime error,這個是什麼緣由呢,咱們看一下,以前創表的語句

原來本來的建表的時候就是設置了datetime類型,那這裏就要使用date函數了,這裏對date函數作一些介紹

網上搜的圖

我以爲上面的圖可以很好的說明經常使用的時間函數處理,無非就是hour(小時),minute(分鐘),second(秒),day(天),week(周),month(月),quarter(季度),year(年度)

那麼這一題,顯然就是要使用year了

SELECT *
FROM student
WHERE YEAR(sage) LIKE '1990%';

好的,很完美的解決了問題

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

號升序排列

解題思路:按照課程分組,而後求分組之後的平均值。而後外加一個排序,排序順序按照課程id升序排列

知識點:group by ,order by

SELECT cid,AVG(score)
FROM sc
GROUP BY cid
ORDER BY AVG(score) DESC,cid;

因爲這個表裏面只有3門課,因此這裏顯示不出來,若是有不少數據,而後又剛好有相同的id

的時候應該就能夠看出不一樣了

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

解題思路:按照學生id分組,而後求平均分,而後用having語句判斷平均分大於等於85,又由於要查詢學生姓名所以可使用join,或者直接使用笛卡爾積,可是笛卡爾積對於數據量比較大的時候不宜使用

知識點:having,groupby,join

SELECT sc.sid,sname,AVG(score) AS 平均成績
FROM sc,student
WHERE sc.sid = student.sid
GROUP BY sc.sid
HAVING 平均成績>= 85;

 

或者

SELECT sc.sid,sname,AVG(score) AS 平均成績
FROM sc INNER JOIN student ON sc.sid = student.sid
GROUP BY sc.sid
HAVING 平均成績>= 85;

結果都是第同樣的,可是數據裏大的時候我會使用第二種解法

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

解題思路:和上一題同樣,可使用join,也能夠直接使用笛卡爾積,可是要使用三張表,因此最好仍是使用join

知識點:多重join的使用

SELECT 
  sname,
  score 
FROM
  course AS c 
  INNER JOIN sc 
    ON c.cid = sc.cid 
    AND c.cname = '數學' 
    AND sc.score < 60 
  INNER JOIN student AS s 
    ON sc.sid = s.sid ;

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

解題思路:要把學生的姓名展現出來,因此使用join

知識點:join的使用

SELECT 
  sname,
  sc.cid,
  score 
FROM
  sc 
  INNER JOIN student AS s 
    ON sc.sid = s.sid ;

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

解題思路:就是在上一題的基礎上,加上一個where條件判斷,或者直接在join on後邊判斷

知識點:join加上條件判斷

SELECT 
  sname,
  sc.cid,
  score 
FROM
  sc 
  INNER JOIN student AS s 
    ON sc.sid = s.sid 
    AND score > 70 ;

或者

SELECT 
  sname,
  sc.cid,
  score 
FROM
  sc 
  INNER JOIN student AS s 
    ON sc.sid = s.sid 
WHERE score > 70 ;

結果是同樣的,至於二者的區別,能夠參考下面這篇文章https://blog.csdn.net/weixin_40539892/article/details/90263677

總結起來就是inner join後邊時兩個作法是沒有區別的,可是若是是left join這種就會有多餘的出來了,這是由於使用join之後會生成一個虛擬表,而使用where是在這張虛擬表的基礎上進行篩選,因此結果可能不同。這個值得注意。

30.查詢不及格的課程

解題思路:這個其實和上面一題是同一個考法,不詳細展開,惟一的區別就是隻查詢課程,因此就是要distinct,或者groupby

知識點:join,

SELECT 
sc.cid
FROM
  sc 
  INNER JOIN student AS s 
    ON sc.sid = s.sid 
WHERE score < 60 
GROUP BY sc.cid

第二種使用distinct

SELECT 
DISTINCT sc.cid
FROM
  sc 
  INNER JOIN student AS s 
    ON sc.sid = s.sid 
WHERE score < 60 ;

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

解題思路:首先確定須要join把兩個表連起來,而後使用條件查詢課程id爲01且成績大於80

知識點:join where

SELECT sc.sid,sname
FROM sc INNER JOIN student AS s
ON sc.sid = s.sid
WHERE sc.cid = 01 AND score >= 80;

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

解題思路:就是簡單的按照課程分組而後求sid的人數

知識點:group by ,count

SELECT cid,COUNT(sid) AS 人數
FROM sc
GROUP BY cid;

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

解題思路:這題首先要找到張三老師所授課程,而後找到選擇這門課程的學生,在成績按照從大到小的順序排列,而後只取最高的成績,固然還須要使用子查詢

知識點:join,limit

SELECT 
  s.*,
  score 
FROM
  student AS s 
  INNER JOIN sc 
    ON s.sid = sc.sid 
WHERE sc.cid = 
  (SELECT 
    cid 
  FROM
    teacher AS t 
    INNER JOIN course AS c 
      ON t.tid = c.cid 
      AND tname = '張三') 
ORDER BY score DESC 
LIMIT 1 ;

如今慢慢有點感受了,可以很快寫出來,果真這種語言相關的仍是要多敲代碼,不要怕錯,就是烏龜的速度,只要一直往前也有機會超過兔子

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

信息及其成績

解題思路:有重複成績,咱們就選擇一個最大成績而後使用子查詢,讓成績在各科成績的最大值裏面作篩選

知識點:多重join,以及子查詢的應用

SELECT 
  student.*,
  sc.cid,
  score 
FROM
  student 
  INNER JOIN sc 
    ON student.sid = sc.sid 
  JOIN course 
    ON sc.cid = course.cid 
  JOIN teacher 
    ON course.tid = teacher.tid 
WHERE tname = '張三' 
  AND score IN 
  (SELECT 
    MAX(score) 
  FROM
    sc 
    INNER JOIN course 
      ON sc.cid = course.cid 
    JOIN teacher 
      ON course.tid = teacher.tid 
  WHERE tname = '張三') ;

因爲我建立的表格裏面沒有重複的成績,因此返回結果沒有發生變化

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

解題思路:首先join兩張表,而後作篩選1成績相同,2課程id不相同

知識點:join,以及where的使用

SELECT 
  DISTINCT a.* 
FROM
  sc AS a 
  INNER JOIN sc AS b 
WHERE a.score = b.score 
  AND a.cid != b.cid ;

因爲可能使用的是inner join,因此可能會有不少重複值,所以我用了一個distinct的作法。

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

解題思路:這題和前面有一題有很類似的地方,就是找到大於某個成績只有兩人就是前兩名

知識點:關聯子查詢

SELECT 
  * 
FROM
  sc 
WHERE 
  (SELECT 
    COUNT(*) 
  FROM
    sc AS a 
  WHERE sc.cid = a.cid 
    AND sc.score < a.score) < 2 
ORDER BY cid ASC,
  sc.score DESC ;

我以爲這個題仍是相對來講好理解的,一樣的查詢前3名等等,只需改一個數字。

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

解題思路:按照課程分組,而後count人數,使用having作一個篩選

知識點:groupby having

SELECT 
  cid,
  COUNT(sid) 
FROM
  sc 
GROUP BY cid 
HAVING COUNT(sid) > 5 ;

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

解題思路:和上一題基本相似按照學生id分組而後count cid,篩選出大於等於2的學生

知識點:groupby having

SELECT 
  sid,
  COUNT(cid) 
FROM
  sc 
GROUP BY sid 
HAVING COUNT(cid) >= 2 ;

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

解題思路:一樣的咱們要知道總共有多少門課,不光是爲了回答這一道題,可能真實狀況會有不少的課,那麼就須要把course表中全部的課程計數,還要求學生信息,那就須要使用join了

知識點:join,where,子查詢

SELECT 
  s.* 
FROM
  sc 
  INNER JOIN student AS s 
    ON sc.sid = s.sid 
WHERE cid = 
  (SELECT 
    COUNT(*) 
  FROM
    course) ;

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

解題思路:這題開始就要使用時間函數了,不難,還算比較簡單

知識點:date函數的使用

SELECT 
  sname,
  YEAR(NOW()) - YEAR(sage) AS 年紀 
FROM
  student ;

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

解題思路:這個可使用case when語句,而後使用時間戳求差值

知識點:case when ,year,date_format

SELECT 
  sname,
  CASE
    WHEN (
      DATE_FORMAT(NOW(), '%m-%d') - DATE_FORMAT(sage, '%m-%d')
    ) < 0 
    THEN YEAR(NOW()) - YEAR(sage) + 1 
    ELSE YEAR(NOW()) - YEAR(sage) 
  END AS 年齡 
FROM
  student ;

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

解題思路:使用week函數

知識點:week,now

SELECT sname
FROM student
WHERE WEEK(sage) = WEEK(NOW());

能夠發現沒有結果,爲了驗證準確性,咱們把student表格單獨拉出來

SELECT 
  * 
FROM
  student ; 

能夠看到果真沒有,那麼爲了進一步驗證,咱們向student表中新增一行數據

INSERT INTO Student VALUES('09' , '關羽' , '1990-07-15' , '');
SELECT 
  * 
FROM
  student ;

看一下執行結果

在執行如下咱們以前的語句

ok看來語句是沒有毛病的,我也不知道爲啥非要驗證如下,多是強迫症吧。。。

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

解題思路:好的嘛,下週過生日,表明着在上題的基礎上再加1

知識點:week,now

SELECT 
  sname 
FROM
  student 
WHERE WEEK(sage) = WEEK(NOW()) + 1 ;

固然仍是沒有結果的,不過這裏就不作測試了

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

解題思路:基本相似的操做,使用month函數

知識點:month,now

SELECT 
  sname,
  sage 
FROM
  student 
WHERE MONTH(sage) = MONTH(NOW());

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

解題思路:一樣的套路,一樣的解法

知識點:month,now

SELECT 
  sname,
  sage 
FROM
  student 
WHERE MONTH(sage) = MONTH(NOW()) + 1 ;

哇哦,不知不覺間就到了最後一題了,這裏給我印象最深的是那幾個須要排序的題,我以爲排序的題,要好好琢磨,爭取可以很快的寫出來。

人果真都是有惰性的,這一篇沒想到拖了這麼久,不過可以在工做休息之餘寫完這篇文仍是很開心的,接下來,要繼續刷題,下一篇應該就是牛客網和leetcode上面的題了。加油!

話說寫了這麼長的文,不給個贊嗎?

相關文章
相關標籤/搜索