MYSQL經(jīng)典45題1刷

Mysql 經(jīng)典練習(xí)題

我使用的Mysql版本是5.7.27-log,答案可能會(huì)因版本會(huì)有少許出入。

一、數(shù)據(jù)源準(zhǔn)備及說(shuō)明:

1.1、數(shù)據(jù)字段說(shuō)明:

1.學(xué)生表 Student(SId,Sname,Sage,Ssex)
SId :學(xué)生編號(hào)
Sname:學(xué)生姓名
Sage :出生年月
Ssex:學(xué)生性別

2.課程表 Course(CId,Cname,TId)
CId :課程編號(hào)
Cname :課程名稱(chēng)
TId :教師編號(hào)

3.教師表 Teacher(TId,Tname)
TId :教師編號(hào)
Tname :教師姓名

4.成績(jī)表 SC(SId,CId,score)
SId :學(xué)生編號(hào)
CId :課程編號(hào)
score: 分?jǐn)?shù)

1.2、將數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù):

導(dǎo)入數(shù)據(jù)方法:
1、將以下 mysql 語(yǔ)句,完整復(fù)制到 workbench 語(yǔ)句窗口(或者是mysql 的黑窗口或者Navicat窗口也可以)
2、然后運(yùn)行即可導(dǎo)入,不需要另外創(chuàng)建表,下面表的操作一樣。
注釋?zhuān)哼@些語(yǔ)句第一條是創(chuàng)建表(create table),后面都是插入數(shù)據(jù)到表中(insert into table ):

學(xué)生表 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' , '錢(qián)電' , '1990-12-21' , '男');
insert into Student values('03' , '孫風(fēng)' , '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('09' , '張三' , '2017-12-20' , '女');
insert into Student values('10' , '李四' , '2017-12-25' , '女');
insert into Student values('11' , '李四' , '2017-12-30' , '女');
insert into Student values('12' , '趙六' , '2017-01-01' , '女');
insert into Student values('13' , '孫七' , '2018-01-01' , '女');

科目表 Course:

create table Course(CId varchar(10),Cname nvarchar(10),TId varchar(10));
insert into Course values('01' , '語(yǔ)文' , '02');
insert into Course values('02' , '數(shù)學(xué)' , '01');
insert into Course values('03' , '英語(yǔ)' , '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' , '王五');

成績(jī)表 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);

二、練習(xí)題目:

1、查詢(xún)" 01 "課程比" 02 "課程成績(jī)高的學(xué)生的信息及課程分?jǐn)?shù)
分析:題意就是想查詢(xún)所有學(xué)生中01課程分?jǐn)?shù)比02課程分?jǐn)?shù)高的學(xué)生有哪些?
思路:分別查出01和02的sid和分?jǐn)?shù),然后與student表內(nèi)連接查詢(xún)學(xué)生姓名,輸出樣式:Sid,Sname,score01,score02,然后用where條件a.score > b.score過(guò)濾一下即可實(shí)現(xiàn)查詢(xún)結(jié)果。

SELECT a.sid
      ,st.Sname
      ,a.score
      ,b.score
FROM(SELECT SId
           ,score
      FROM sc
      WHERE CId = 01) AS a  # 查詢(xún)01課程的分?jǐn)?shù)
INNER JOIN (SELECT SId
                  ,score
            FROM sc
            WHERE CId = 02) AS b ON a.sid = b.sid  # 查詢(xún)02課程的分?jǐn)?shù)
INNER JOIN student AS st ON st.SId = a.SId # 2個(gè)內(nèi)連接查交集
WHERE a.score > b.score;

1.1、查詢(xún)同時(shí)學(xué)過(guò)" 01 "課程和" 02 "課程的學(xué)生信息
思路:使用子查詢(xún),先分別查詢(xún)出01課程和02課程的sid,然后將這兩個(gè)表內(nèi)連接,即是同時(shí)學(xué)01和02課程的sid,此時(shí)再與studen表中的sid匹配查到學(xué)生詳細(xì)信息。

解法1:標(biāo)量子查詢(xún)
SELECT student.*
FROM student 
WHERE student.SId IN (SELECT a.SId
                       FROM (SELECT sid
                             FROM sc 
                             WHERE CId = "01") AS a
                       INNER JOIN (SELECT *
                                   FROM sc 
                                   WHERE CId = "02")  AS b ON a.SId = b.SId);

但這個(gè)查詢(xún)結(jié)果僅僅只能看到學(xué)生信息,看不到學(xué)生各個(gè)課程的分?jǐn)?shù),輸出樣式不是最優(yōu),所以我們希望輸出的樣式是這樣的:sid,sname,score01,score02,那么就可以如題1一樣,使用多表連接對(duì)三個(gè)表做內(nèi)連接,然后直接取需要的字段即可:

SELECT a.sid
      ,st.Sname
      ,a.score
      ,b.score
FROM(SELECT SId
           ,score
      FROM sc
      WHERE CId = 01) AS a
INNER JOIN (SELECT SId
                  ,score
            FROM sc
            WHERE CId = 02) AS b ON a.sid = b.sid
INNER JOIN student AS st ON st.SId = a.SId;

1.2、查詢(xún)存在" 01 "課程但可能不存在" 02 "課程的情況(不存在時(shí)顯示為 null )
分析:所謂"情況",無(wú)非就是查這樣一些人:必須學(xué)過(guò)01課課程,不一定學(xué)過(guò)02課程的學(xué)生是哪些,那既然知道了學(xué)生id,那很有可能想知道這些學(xué)生的信息,所以我們希望查詢(xún)結(jié)果以這樣的格式進(jìn)行輸出:sid,sname,score01,score02.
思路:多表連接的時(shí)候使用左連接即可,分別查詢(xún)出學(xué)過(guò)01和02課程的sid和分?jǐn)?shù),然后將兩個(gè)查詢(xún)結(jié)果進(jìn)行左連接,確保01課程的所有人都在,然后與student表左連接,這樣就得到了想要查詢(xún)結(jié)果了。

SELECT a.sid
      ,st.Sname
      ,a.score
      ,b.score
FROM(SELECT SId
           ,score
      FROM sc
      WHERE CId = 01) AS a
LEFT JOIN (SELECT SId
                  ,score
            FROM sc
            WHERE CId = 02) AS b ON a.sid = b.sid
LEFT JOIN student AS st ON st.SId = a.SId;

1.3、查詢(xún)不存在" 01 "課程但存在" 02 "課程的情況
思路:過(guò)濾掉cid = 01 課程的所有sid,再選取cid = 02的學(xué)生
解法1:使用子查詢(xún)過(guò)濾掉學(xué)過(guò)01課程的學(xué)生,然后再選出那些學(xué)了02課程的學(xué)生,用AND將兩個(gè)約束條件進(jìn)行連接就可以得到最終的結(jié)果集了。

SELECT a.sid
      ,b.sname
      ,a.cid
      ,a.score
FROM(SELECT * 
     FROM sc 
     WHERE SId NOT IN (SELECT SId 
                       FROM sc 
                       WHERE CId='01') AND CId='02')  AS a #查詢(xún)滿(mǎn)足要求的sid,cid,score
INNER JOIN student AS b ON a.sid = b.sid; # 查詢(xún)對(duì)應(yīng)的學(xué)生信息

解法2:雖然結(jié)果一樣,但寫(xiě)法感覺(jué)別扭,再回頭看時(shí)自己都忘了咋寫(xiě)出來(lái)的了

SELECT b.*
FROM(SELECT * 
     FROM sc 
     GROUP BY sid 
     HAVING cid != "01") AS a
INNER JOIN(SELECT * 
           FROM sc
           WHERE cid = "02") AS b ON a.sid = b.sid
GROUP BY b.sid;

2、查詢(xún)平均成績(jī)大于等于 60 分的同學(xué)的學(xué)生編號(hào)、學(xué)生姓名、平均成績(jī)
思路:內(nèi)連接student表和sc表,然后按照sid分組計(jì)算平均成績(jī)且過(guò)濾掉那些平均分小于60分的人
解法一:使用子查詢(xún),先查詢(xún)出平均成績(jī)大于等于60的學(xué)生的sid和平均分,然后與student表內(nèi)連接獲取學(xué)生姓名字段。

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

解法二:先對(duì)表sc和student內(nèi)連接,然后按照sid分組計(jì)算后取值,這里需要注意的點(diǎn)是student和sc表是一對(duì)多的關(guān)系,進(jìn)行內(nèi)連接之后,新表里student的記錄是自動(dòng)補(bǔ)全的,如下圖:


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

3、查詢(xún)?cè)?SC 表存在成績(jī)的學(xué)生信息
思路:使用子查詢(xún),先在sc表按sid去重看看sc表都有哪些sid,然后在student表中查詢(xún)對(duì)應(yīng)sid的學(xué)生信息即可

SELECT student.*
FROM student
WHERE sid IN (SELECT DISTINCT sid 
              FROM sc)
GROUP BY sid; # 以防萬(wàn)一再在student表中按sid group by去重一下。

4、查詢(xún)所有同學(xué)的學(xué)生編號(hào)、學(xué)生姓名、選課總數(shù)、所有課程的總成績(jī)(沒(méi)成績(jī)的顯示為 0 )
思路:student和sc表左連接之后,按照sid、sname分組統(tǒng)計(jì)取需要字段即可

SELECT st.SId
      ,st.Sname
      ,COUNT(DISTINCT s.CId)
      ,SUM(CASE WHEN s.score IS NULL THEN 0 ELSE s.score END) # 這里用到的的case when是需要特別注意的技巧
FROM student AS st 
LEFT  JOIN sc AS s ON st.SId = s.SId
GROUP BY st.SId,st.Sname
ORDER BY st.SId

注意:student表和sc表進(jìn)行做連接后的結(jié)果集如下:


image.png

4.1 、查有成績(jī)的學(xué)生信息
思路:先看sc表中有成績(jī)的sid是哪些,然后就可以根據(jù)sid查看學(xué)生信息了
解法一:使用子查詢(xún) IN

SELECT *
FROM student AS a
WHERE a.sid IN (SELECT DISTINCT sid
                FROM sc
                GROUP BY sid);

解法二:如上題,對(duì)student表和sc表左內(nèi)連接之后,按照sid進(jìn)行分組去重,得到學(xué)生信息

SELECT st.*
FROM student AS st
INNER JOIN sc AS s ON st.sid = s.sid
GROUP BY s.sid
ORDER BY s.sid

5、查詢(xún)「李」姓老師的數(shù)量
思路:通配符和聚合函數(shù)的使用,這題就很簡(jiǎn)單,沒(méi)什么說(shuō)的

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

6、查詢(xún)學(xué)過(guò)「張三」老師授課的同學(xué)的信息
思路:多表連接或者子查詢(xún)
解法一:使用子查詢(xún)書(shū)寫(xiě)

SELECT *
FROM student
WHERE sid  IN (SELECT sid
               FROM sc
               WHERE cid = (SELECT cid 
                             FROM course
                             WHERE tid = (SELECT tid 
                                          FROM teacher
                                          WHERE tname = "張三")));

解法二:直接內(nèi)連接所有需要用到的表

SELECT st.SId
      ,st.Sname
      ,st.sage
      ,st.ssex
      ,s.cid
      ,s.score
      ,c.cname
      ,t.tid
      ,t.tname  # 字段選取可根據(jù)需要去留
FROM student AS st 
INNER JOIN sc AS s ON st.SId = s.SId
INNER JOIN course AS c ON c.CId = s.CId
INNER JOIN teacher AS t ON t.TId = c.TId
WHERE t.Tname = '張三';
查詢(xún)結(jié)果

注意點(diǎn):所有表內(nèi)連接之后的結(jié)果集:可以看到是以數(shù)據(jù)量最多的表格為準(zhǔn),就知道有幾行數(shù)據(jù)了


image.png

6.1、查詢(xún)沒(méi)學(xué)過(guò)張三老師課程的學(xué)生的學(xué)號(hào)和姓名
思路:所有學(xué)過(guò)張三老師課程的學(xué)生進(jìn)行取反

SELECT *
FROM student
WHERE SId NOT IN(SELECT st.SId
                 FROM student AS st 
                 INNER JOIN sc AS s ON st.SId = s.SId
                 INNER JOIN course AS c ON c.CId = s.CId
                 INNER JOIN teacher AS t ON t.TId = c.TId
                 WHERE t.Tname = '張三');

7、查詢(xún)沒(méi)有學(xué)全所有課程的同學(xué)的信息
思路:將student表和sc表進(jìn)行左連接,這樣不會(huì)漏掉一門(mén)課也沒(méi)有學(xué)的同學(xué),然后按照sid分組,統(tǒng)計(jì)分組后的同學(xué)的課程數(shù)量,如果小于總的課程數(shù)量,那么就是沒(méi)學(xué)全

SELECT st.SId
      ,st.Sname
      ,COUNT(DISTINCT s.CId)
FROM student AS st
LEFT JOIN sc AS s ON st.SId = s.SId
GROUP BY st.SId
HAVING COUNT(DISTINCT s.CId) < (SELECT COUNT(DISTINCT CId)
                                FROM course);

8、查詢(xún)至少有一門(mén)課與學(xué)號(hào)為" 01 "的同學(xué)所學(xué)課程相同的同學(xué)的信息
思路:子查詢(xún)嵌套,先找01號(hào)學(xué)生的課程,再找包含在這里面的其他sid,然后查信息,需要注意的是最后還要排除01號(hào)這個(gè)sid。
解法一:使用子查詢(xún)

SELECT SId
      ,Sname
FROM student
WHERE SId IN(SELECT SId
             FROM sc
             WHERE cid IN(SELECT cid
                          FROM sc
                          WHERE SId = 01)) AND SId <> 01;

解法二:外層student表和子查詢(xún)結(jié)果表內(nèi)連接,提升查詢(xún)效率,具體如下:
注釋?zhuān)簲?shù)據(jù)較多時(shí),inner join查詢(xún)效率比子查詢(xún)高

select a.sid
      ,a.sname
FROM student AS a 
INNER JOIN(SELECT distinct sid 
            FROM sc
            WHERE cid in (SELECT cid
                           FROM sc
                           WHERE sid = "01") AND sid <> "01")  AS b ON a.sid =b.sid;

解法三:使用多表連接查詢(xún)的思路也是不錯(cuò)的,直接右連接student表和sc表(此題目中其實(shí)用什么類(lèi)型的連接結(jié)果都是一樣的),因?yàn)楸黻P(guān)系是一對(duì)多,所以直接使用過(guò)濾條件去重查詢(xún),然后再去掉01號(hào)同學(xué)就可以了

SELECT distinct b.* 
FROM sc a 
LEFT JOIN student b 
ON a.sid=b.sid 
WHERE cid IN (SELECT cid 
              FROM sc 
              WHERE sid='01') AND a.sid != "01";

9、查詢(xún)和" 01 "號(hào)同學(xué)學(xué)習(xí)的課程 完全相同的其他同學(xué)的信息。
思路:先查詢(xún)出與01號(hào)同學(xué)所學(xué)課程完全不同的學(xué)生的sid,然后對(duì)其取反,那么得到的就是與01號(hào)同學(xué)所學(xué)課程至少有一門(mén)課程相同的學(xué)生,如果此時(shí)他們所學(xué)的課程數(shù)量又相等,那么該學(xué)生必然與01號(hào)同學(xué)所學(xué)課程是完全相同的

SELECT student.*
FROM student
WHERE SId IN(SELECT SId
             FROM sc
             WHERE SId <> 01 # 先過(guò)濾掉01號(hào)同學(xué)本身
             GROUP BY SId
             HAVING COUNT(DISTINCT CId) = (SELECT COUNT(DISTINCT CId)       #計(jì)算01號(hào)同學(xué)所學(xué)課程數(shù)量
                                           FROM sc 
                                           WHERE SId = 01) AND SId NOT IN(SELECT SId# 再取反排除這些與01號(hào)同學(xué)所學(xué)課程完全不同的學(xué)生                                                                
                                                                          FROM sc # 對(duì)下級(jí)查詢(xún)結(jié)果取反,得到哪些跟01號(hào)同學(xué)所學(xué)課程完全不同的學(xué)生編號(hào)   
                                                                          WHERE CId NOT IN(SELECT CId                                                                                                                                                                                                                                                   
                                                                                           FROM sc
                                                                                           WHERE SId = '01');#查01號(hào)同學(xué)學(xué)了哪幾門(mén)課程的編號(hào)

-- 這題還是蠻有意思的,思路不錯(cuò)

10、查詢(xún)沒(méi)學(xué)過(guò)"張三"老師講授的任一門(mén)課程的學(xué)生姓名
思路:查詢(xún)出“張三”老師所教的課程id有哪些,然后看誰(shuí)選了他教的課程,然后對(duì)其取反即可,就是那些一門(mén)都沒(méi)有學(xué)習(xí)張三老師課程的學(xué)生信息。

SELECT *
FROM student
WHERE SId NOT IN(SELECT st.SId
                 FROM student AS st 
                 INNER JOIN sc AS s ON st.SId = s.SId
                 INNER JOIN course AS c ON c.CId = s.CId
                 INNER JOIN teacher AS t ON t.TId = c.TId
                 WHERE t.Tname = '張三');

思考:假設(shè)使用子查詢(xún),先查詢(xún)出張三老師所教課程的全部編號(hào),然后在sc表中對(duì)這個(gè)子查詢(xún)結(jié)果進(jìn)行取反,然后根據(jù)sc表中的sid是否就可以查到學(xué)生的信息了呢?
答:不可以,因?yàn)樵趕c表中進(jìn)行排除的時(shí)候,比如01號(hào)同學(xué)同時(shí)學(xué)些了01/02/03課程,使用where條件過(guò)濾掉02,實(shí)際上01號(hào)同學(xué)還是被選出來(lái)了。那用先分組再having過(guò)濾呢?(我的答案是不可以)

10.1、查詢(xún)學(xué)過(guò)張三老師所教的所有課程的同學(xué)的學(xué)號(hào)和姓名
思路:只要明白多表連接之后的“表”是什么樣子的就很容易寫(xiě)出答案來(lái)了

SELECT st.SId
      ,st.Sname
FROM student AS st 
INNER JOIN sc AS s ON st.SId = s.SId
INNER JOIN course AS c ON c.CId = s.CId
INNER JOIN teacher AS t ON t.TId = c.TId
WHERE t.Tname = '張三';

11、查詢(xún)兩門(mén)及以上不及格課程的同學(xué)的學(xué)號(hào),姓名及其平均成績(jī)
思路:先查出成績(jī)小于60的所有記錄,然后按sid分組,統(tǒng)計(jì)課程數(shù)大于等于2的sid是哪些,然后內(nèi)連接student和sc ,然后按sid,sname分組,計(jì)算分組后某個(gè)sid的平均分

SELECT st.sid
      ,st.sname
      ,AVG(s.score)
FROM student AS st
INNER JOIN sc AS s ON st.sid = s.sid
WHERE st.SId IN(SELECT SId
                FROM sc
                WHERE score < 60
                GROUP BY SId 
                HAVING COUNT(DISTINCT CId) >= 2)
GROUP BY st.sid,st.sname;

12、檢索" 01 "課程分?jǐn)?shù)小于 60,按分?jǐn)?shù)降序排列的學(xué)生信息
思路:直接內(nèi)連接student和sc表,然后where過(guò)濾條件,按分?jǐn)?shù)降序排列(子查詢(xún)也可以實(shí)現(xiàn),這里就不寫(xiě)具體代碼了)

SELECT st.*
      ,s.CId
     ,s.score
FROM sc AS s 
INNER JOIN student AS st ON s.SId = st.SId
WHERE s.CId = 01 AND s.score <60
ORDER BY s.score DESC;
13、按平均成績(jī)從高到低顯示所有學(xué)生的所有課程的成績(jī)以及平均成績(jī)

思路:可以使用子查詢(xún),也可以使用case when
解法一:使用子查詢(xún),先查詢(xún)出學(xué)生平均分,然后與sc表內(nèi)連接,這樣連接之后的表每一條數(shù)據(jù)后都有了平均分,然后排序就好了。

SELECT s.SId
      ,s.score
      ,a.avg_score
FROM sc AS s
INNER JOIN (SELECT SId
                  ,AVG(score) AS avg_score
            FROM sc 
            GROUP BY SId) AS a ON s.SId = a.SId
ORDER BY a.avg_score DESC;

值得注意的是:這樣寫(xiě)結(jié)果是出來(lái)了,但顯示方式不是很好(這里其最重要的作用是驗(yàn)證多表聯(lián)結(jié)時(shí),出現(xiàn)多對(duì)一的情況的時(shí)候,“一”會(huì)在后面自動(dòng)補(bǔ)全)

推薦使用下面這種寫(xiě)法:展示樣式為:sid-語(yǔ)文-數(shù)學(xué)-英語(yǔ)-平均分,更符合實(shí)際業(yè)務(wù)場(chǎng)景
SELECT SId
      ,MAX(CASE WHEN CId = 01 THEN score ELSE NULL END) AS 語(yǔ)文
      ,MAX(CASE WHEN CId = 02 THEN score ELSE NULL END) AS 數(shù)學(xué)
      ,MAX(CASE WHEN CId = 03 THEN score ELSE NULL END) AS 英語(yǔ)
      ,AVG(score) AS 平均分
FROM sc
GROUP BY SId
ORDER BY  平均分 DESC;
這里的case when用法不錯(cuò),因?yàn)橹挥幸粋€(gè)值,用max、avg等其實(shí)是一樣的結(jié)果。

14、查詢(xún)各科成績(jī)最高分、最低分和平均分: 以如下形式顯示:

  • 課程 ID,課程 name,最高分,最低分,平均分,及格率,中等率,優(yōu)良率,優(yōu)秀率。
  • 及格為>=60,中等為:70-80,優(yōu)良為:80-90,優(yōu)秀為:>=90
  • 要求輸出課程號(hào)和選修人數(shù),查詢(xún)結(jié)果按人數(shù)降序排列,若人數(shù)相同,按課程號(hào)升序排列。
    思路:實(shí)際就是按照cid進(jìn)行分組統(tǒng)計(jì),查看每門(mén)課程的最大、最小、平均值,及相應(yīng)的XX率。從題意得知需要用到的表為sc表和course表,所以直接將這兩個(gè)表做一個(gè)內(nèi)連接,然后進(jìn)行取字段求值。
這里特別要注意的是case when的用法,當(dāng)用sum進(jìn)行人數(shù)統(tǒng)計(jì)的時(shí)候,需要分門(mén)別類(lèi)的進(jìn)行數(shù)量統(tǒng)計(jì),所以就想到使用case when來(lái)進(jìn)行分類(lèi)匯總。
SELECT s.cid
      ,c.cname
      ,MAX(s.score)
      ,MIN(s.score)
      ,AVG(s.score)
      ,CONCAT(TRUNCATE(SUM(CASE WHEN s.score >= 60 THEN 1 ELSE 0 END)/COUNT(DISTINCT s.sid) * 100,2),"%") AS 及格率
      ,CONCAT(TRUNCATE(SUM(CASE WHEN s.score >= 70 AND s.score < 80 THEN 1 ELSE 0 END)/COUNT(DISTINCT s.sid),2) * 100,"%") AS 中等率
      ,CONCAT(TRUNCATE(SUM(CASE WHEN s.score >= 80 AND s.score < 90 THEN 1 ELSE 0 END)/COUNT(DISTINCT s.sid),2) * 100,"%") AS 優(yōu)良率
      ,CONCAT(TRUNCATE(SUM(CASE WHEN s.score >= 90 THEN 1 ELSE 0 END)/COUNT(DISTINCT s.sid) * 100,2),"%") AS 優(yōu)秀率
FROM sc AS s
INNER JOIN course AS c ON s.cid = c.cid
GROUP BY s.cid;

注意:還需要注意的是concat函數(shù)的用法和truncate的用法,與truncate(不進(jìn)行四舍五入)用法類(lèi)似的函數(shù)round(四舍五入)的區(qū)別。


區(qū)別實(shí)例

15、按各科成績(jī)進(jìn)行排序,并顯示排名, Score 重復(fù)時(shí)名次空缺 (1224格式)
思路:用窗口函數(shù),需Mysql8.0以上版本

SELECT CId
      ,RANK() OVER (PARTITION BY CId ORDER BY score DESC) AS rank_num
FROM sc;

15.1 按各科成績(jī)進(jìn)行排序,并顯示排名, Score 重復(fù)時(shí)合并名次(假設(shè)是1223類(lèi)型的排序)
分析:窗口函數(shù),需Mysql8.0以上版本

SELECT CId
      ,score
      ,DENSE_RANK() over(PARTITION BY CId ORDER BY score DESC) AS rank
FROM sc;

16、查詢(xún)學(xué)生的總成績(jī),并進(jìn)行排名,總分重復(fù)時(shí)名次空缺 (假設(shè)按照1224)
解法一:窗口函數(shù),需Mysql8.0以上版本

SELECT SId
       ,total_score
       ,RANK() OVER (ORDER BY total_score DESC) AS rank_num
FROM(SELECT SId
           ,SUM(score) AS total_score
     FROM sc
     GROUP BY SId) AS t;

解法二:定義變量:

SELECT SId
      ,sum_score
      ,@rank := IF(@score1 = sum_score,@rank,@rank + 1) AS rank_num
      ,@score1 := sum_score AS 總成績(jī)  # 保存上一次的分?jǐn)?shù)
FROM (SELECT SId
            ,SUM(score) AS sum_score
      FROM sc
      GROUP BY SId
      ORDER BY SUM(score) DESC) AS a
JOIN (SELECT @rank := '',@score1 := '') AS b;

也可以用case when的語(yǔ)法:
將:
@rank := IF(@score1 = sum_score,@rank,@rank + 1) AS rank_num
替換成:
CASE WHEN @score1 = sum_score THEN @rank := @rank ELSE @rank := @rank + 1 END AS 名次。
跟用IF邏輯是一樣的,都是做判斷,具體如下:

SELECT SId
      ,sum_score
      ,CASE WHEN @score1 = sum_score THEN @rank := @rank ELSE @rank := @rank + 1 END AS 名次
      ,@score1 := sum_score AS 總成績(jī)
FROM (SELECT SId
            ,SUM(score) AS sum_score
      FROM sc
      GROUP BY SId
      ORDER BY SUM(score) DESC) AS a
JOIN (SELECT @rank := '',@score1 := '') AS b;

總結(jié):

需要定義兩個(gè)空變量,@rank變量用于排名,@score1變量用于與總成績(jī)進(jìn)行比較,從而得到排名,需要特別注意的是“”@score1 := sum_score AS 總成績(jī)“”必須要寫(xiě),因?yàn)檫@一步是將比較后的值賦給@score1,以便后續(xù)的比較排名。

16.1、 查詢(xún)學(xué)生的總成績(jī),并進(jìn)行排名,總分重復(fù)時(shí)名次不空缺(1223)
解法一:窗口函數(shù):需Mysql8.0以上版本

SELECT SId
       ,total_score
       ,DENSE_RANK() OVER (ORDER BY total_score DESC) AS rank_num
FROM(SELECT SId
           ,SUM(score) AS total_score
     FROM sc
     GROUP BY SId) AS t;

解法二:定義變量:所謂空缺不空缺到底啥意思?這里我分兩種情況寫(xiě):

# 1.假設(shè)直接1234排序:結(jié)果同ROW_NUMBER()
SET @rank = 0;
SELECT SId
      ,total_score
      ,@rank := @rank + 1 AS rank_num
FROM (SELECT SId
            ,SUM(score) AS total_score
      FROM sc
      GROUP BY SId
      ORDER BY SUM(score) DESC) AS t;


# 2.假設(shè)是1223的順序:
SELECT SId
      ,sum_score
      ,CASE WHEN @score1 = sum_score THEN @rank := @rank ELSE @rank := @rank + 1 END AS 名次
      ,@score1 := sum_score AS 總成績(jī)
FROM (SELECT SId
            ,SUM(score) AS sum_score
      FROM sc
      GROUP BY SId
      ORDER BY SUM(score) DESC) AS a
JOIN (SELECT @rank := '',@score1 := '') AS b;

17、統(tǒng)計(jì)各科成績(jī)各分?jǐn)?shù)段人數(shù):課程編號(hào),課程名稱(chēng),[100-85],[85-70],[70-60],[60-0] 及所占百分比

SELECT s.cid
      ,c.cname
      ,SUM(CASE WHEN s.score <= 100 AND s.score > 85 THEN 1 ELSE 0 END) AS "[100,85)"
      ,SUM(CASE WHEN s.score <= 85 AND s.score > 70 THEN 1 ELSE 0 END) AS "[85,70)"
      ,SUM(CASE WHEN s.score <= 70 AND s.score > 60 THEN 1 ELSE 0 END) AS "[70,60)"
      ,SUM(CASE WHEN s.score <= 60 THEN 1 ELSE 0 END) AS "(0,60]"
FROM sc AS s
INNER JOIN course AS c ON s.cid = c.cid
GROUP BY s.cid,c.cname;

18、查詢(xún)各科成績(jī)前三名的記錄
解法一:窗口函數(shù),需Mysql8.0以上版本

SELECT *
FROM(SELECT CId
           ,score
           ,ROW_NUMBER() OVER (PARTITION BY CId ORDER BY score DESC) AS rank_num
      FROM sc) AS t
WHERE rank_num <= 3; 

19、查詢(xún)每門(mén)課程被選修的學(xué)生數(shù)

SELECT s.CId
      ,c.Cname
      ,COUNT(DISTINCT s.SId)
FROM sc AS s 
INNER JOIN course AS c ON s.CId = c.CId
GROUP BY s.CId,c.Cname;

20、查詢(xún)出只選修兩門(mén)課程的學(xué)生學(xué)號(hào)和姓名

SELECT s.SId
      ,st.Sname
      ,COUNT(DISTINCT s.CId) AS cnt
FROM sc AS s
INNER JOIN student AS st ON s.SId = st.SId
GROUP BY s.SId,st.Sname
HAVING cnt = 2

也可以使用子查詢(xún),但性能可能較低

SELECT SId
      ,Sname
FROM student 
WHERE SId IN (SELECT SId
             FROM sc 
             GROUP BY SId
             HAVING COUNT(DISTINCT CId) = 2);

21、查詢(xún)男生、女生人數(shù)

SELECT Ssex
      ,COUNT(DISTINCT sid)
FROM student
GROUP BY Ssex;

用case when來(lái)實(shí)現(xiàn)的注意事項(xiàng):

SELECT SUM(CASE WHEN Ssex = "男" THEN 1 else 0 END) AS '男生人數(shù)'
      ,SUM(CASE WHEN Ssex = "女" THEN 1 else 0 END) AS '女生人數(shù)'
FROM student;

需要注意的是如果用count,那么上面的0要改成null才行,因?yàn)閚ull是不參與計(jì)算的,而0參與。

SELECT COUNT(CASE WHEN Ssex = "男" THEN 1 else NULL END) AS '男生人數(shù)'
      ,COUNT(CASE WHEN Ssex = "女" THEN 1 else NULL END) AS '女生人數(shù)'
FROM student;
# 如果將NULL修改為0的話(huà),那么查詢(xún)出來(lái)的結(jié)果無(wú)論男女?dāng)?shù)量都是12,因?yàn)?也被計(jì)入count的計(jì)算中了

22、查詢(xún)名字中含有「風(fēng)」字的學(xué)生信息

SELECT *
FROM student
WHERE Sname LIKE  "%風(fēng)%";

23、查詢(xún)同名同性學(xué)生名單,并統(tǒng)計(jì)同名人數(shù)
解法一:按照姓名分組,姓名形同的情況下按照性別分組統(tǒng)計(jì)人數(shù),如果統(tǒng)計(jì)人數(shù)大于等于2,那說(shuō)明這個(gè)人就是同名同性的

SELECT Sname
      ,Ssex
      ,COUNT(Sname)
FROM student
GROUP BY Sname,Ssex
HAVING COUNT(Sname) >= 2;

解法二:自連接,過(guò)濾條件為性別相同,且sid不相同

SELECT st1.*
      ,COUNT(1) AS cons 
FROM student  AS st1
INNER JOIN student AS st2 ON st1.sname=st2.sname AND st1.ssex=st2.ssex AND st1.sid != st2.sid;

解法三:

select *
from student LEFT JOIN (select Sname
                              ,Ssex,COUNT(*)同名人數(shù) 
                         from Student 
                         group by Sname,Ssex) as t1 on student.Sname =t1.Sname and student.Ssex=t1.Ssex
where t1.同名人數(shù)>1

24、查詢(xún) 1990 年出生的學(xué)生名單

SELECT *
FROM student
WHERE YEAR(Sage) = 1990;

25、查詢(xún)每門(mén)課程的平均成績(jī),結(jié)果按平均成績(jī)降序排列,平均成績(jī)相同時(shí),按課程編號(hào)升序排列

SELECT s.CId
      ,c.Cname
      ,AVG(score) AS avg_score
FROM sc AS s
INNER JOIN course AS c ON c.CId = s.CId
GROUP BY s.CId
        ,c.Cname
ORDER BY avg_score DESC,s.CId ASC;

26、查詢(xún)平均成績(jī)大于等于 85 的所有學(xué)生的學(xué)號(hào)、姓名和平均成績(jī)

SELECT st.SId
      ,st.Sname
      ,AVG(s.score)
FROM sc AS s
INNER JOIN student AS st ON s.SId = st.SId
GROUP BY st.SId,st.Sname
HAVING AVG(s.score) >= 85;

27、查詢(xún)課程名稱(chēng)為「數(shù)學(xué)」,且分?jǐn)?shù)低于 60 的學(xué)生姓名和分?jǐn)?shù)

SELECT st.SId
      ,st.Sname
      ,s.score
      ,c.Cname
FROM student AS st 
INNER JOIN sc AS s ON st.SId = s.SId
INNER JOIN course AS c ON s.CId = c.CId
WHERE c.Cname = "數(shù)學(xué)" AND s.score < 60;

28、查詢(xún)所有學(xué)生的課程及分?jǐn)?shù)情況(存在學(xué)生沒(méi)成績(jī),沒(méi)選課的情況)

 # 按實(shí)際需求希望表頭如:sid sname 語(yǔ)文 數(shù)學(xué) 英語(yǔ) 進(jìn)行展示
SELECT st.SId
      ,st.Sname
      ,MAX(CASE WHEN c.Cname = "語(yǔ)文" THEN s.score ELSE NULL END) AS "語(yǔ)文"  
      ,MAX(CASE WHEN c.Cname = "數(shù)學(xué)" THEN s.score ELSE NULL END) AS "數(shù)學(xué)"
      ,MAX(CASE WHEN c.Cname = "英語(yǔ)" THEN s.score ELSE NULL END) AS "英語(yǔ)"
FROM sc AS s
INNER JOIN course AS c ON s.CId = c.CId
INNER JOIN student AS st ON s.SId = st.SId
GROUP BY st.SId
        ,st.Sname;

需要注意的是:本題如果只按sid,sname分組查詢(xún),成績(jī)只能返回第一個(gè),所以必須用case when進(jìn)行分類(lèi)后逐一進(jìn)行判斷然后返回最大值(當(dāng)然這里用MIN其實(shí)也無(wú)所謂)

29、查詢(xún)?nèi)魏我婚T(mén)課程成績(jī)?cè)?70 分以上的姓名、課程名稱(chēng)和分?jǐn)?shù)
思路:題意應(yīng)該是想查詢(xún)那些任何一門(mén)成績(jī)都在70分以上的人的情況

SELECT s.SId
      ,st.Sname
      ,c.Cname
      ,s.score
FROM sc AS s 
INNER JOIN course AS c ON s.CId = c.CId
INNER JOIN student AS st ON s.SId = st.SId
WHERE s.score > 70;

30、查詢(xún)不及格的課程并按課程編號(hào)從大到小排列
思路:就是查詢(xún)有哪些課程存在不及格的情況,是誰(shuí)。

SELECT s.sid
      ,st.Sname
      ,s.cid
      ,c.cname
      ,s.score
FROM sc AS s 
INNER JOIN student AS st ON s.SId = st.SId
INNER JOIN course AS c ON s.CId = c.CId
WHERE score < 60
ORDER BY s.CId DESC;

31、查詢(xún)課程編號(hào)為 01 且課程成績(jī)?cè)?80 分以上的學(xué)生的學(xué)號(hào)和姓名

SELECT s.SId
      ,st.Sname
      ,s.CId
      ,c.Cname
      ,s.score
FROM sc as s 
INNER JOIN student AS st ON s.SId = st.SId
INNER JOIN course AS c ON s.CId = c.CId
WHERE s.CId = "01" AND s.score > 80;

32、求每門(mén)課程的學(xué)生人數(shù)

SELECT s.CId
      ,c.Cname
      ,COUNT(DISTINCT SId)
FROM sc AS s 
INNER JOIN course AS c ON s.CId = c.CId
GROUP BY s.CId;

33、成績(jī)不重復(fù)的情況下,查詢(xún)選修「張三」老師所授課程的學(xué)生中,成績(jī)最高的學(xué)生信息及其成績(jī)
分析:成績(jī)不重復(fù),排序后取第一個(gè)或者直接取MAX(score)就可以了

SELECT s.SId
      ,st.Sname
      ,s.score
      ,s.CId
      ,t.Tname
FROM sc AS s
INNER JOIN student as st ON s.SId = st.SId
INNER JOIN course AS c ON c.CId = s.CId
INNER JOIN teacher AS t ON t.TId = c.TId
WHERE t.Tname = "張三"
ORDER BY s.score DESC
LIMIT 1;

或者

SELECT s.SId
      ,st.Sname
      ,MAX(s.score)
      ,s.CId
      ,t.Tname
FROM sc AS s
INNER JOIN student as st ON s.SId = st.SId
INNER JOIN course AS c ON c.CId = s.CId
INNER JOIN teacher AS t ON t.TId = c.TId
WHERE t.Tname = "張三";

34、成績(jī)有重復(fù)的情況下,查詢(xún)選修「張三」老師所授課程的學(xué)生中,成績(jī)最高的學(xué)生信息及其成績(jī)
分析:成績(jī)重復(fù)的情況下,那么第一名有可能是多個(gè)人,這時(shí)候用limit限制就不行了,所以使用窗口函數(shù)中的RANK()是完美的解決辦法。

SELECT * #3.1.查看第一名的信息
FROM(SELECT *
           ,RANK() OVER (ORDER BY score desc) AS rank_num #2.對(duì)子查詢(xún)結(jié)果進(jìn)行排名
     FROM(SELECT st.SId #1.先把學(xué)張三老師課的人找出來(lái)
                ,st.Sname
                ,s.score
          FROM student AS st 
          INNER JOIN sc AS s ON st.SId = s.SId
          INNER JOIN course AS c ON c.CId = s.CId
          INNER JOIN teacher AS t ON t.TId = c.TId
          WHERE t.Tname = '張三')AS x)AS y
WHERE rank_num = 1; #3.2. 篩選排名第一的

35、查詢(xún)不同課程成績(jī)相同的學(xué)生的學(xué)生編號(hào)、課程編號(hào)、學(xué)生成績(jī)
思路:意思是查詢(xún)哪些同學(xué)每門(mén)課程成績(jī)都一樣(這里是我的理解),所以先查詢(xún)所學(xué)課程總數(shù)大于1的學(xué)生id,然后與sc表做內(nèi)連接,再按sid和score的組合進(jìn)行分組統(tǒng)計(jì),如果幾率條數(shù)只有一條,那么就意味著這位同學(xué)的不同科目的成績(jī)都是一樣的。

SELECT *
FROM(SELECT a.sid
           ,a.score 
           ,b.cnt
     FROM sc AS a
     INNER JOIN(SELECT SId #查詢(xún)課程數(shù)大于1的學(xué)生id
                      ,COUNT(DISTINCT CId) AS cnt
                FROM sc
                GROUP BY SId
                HAVING cnt > 1) AS b ON a.sid = b.sid
     GROUP BY a.sid
             ,a.score) AS c
GROUP BY sid
HAVING COUNT(sid) = 1;

這里需要特別注意的是:group by 之后的字段的分組明細(xì),必須所有字段值都一樣才會(huì)輸出一條記錄,否則就是多條記錄。

36、查詢(xún)每門(mén)課程成績(jī)最好的前兩名
分析:前兩名,如果碰到成績(jī)一樣的情況,按照實(shí)際需求,應(yīng)該前兩名包含不止2人才對(duì),是名次排在第一和第二的都要包含進(jìn)去才符合實(shí)際,所以這里我用rank()

# 使用窗口函數(shù)
SELECT *
FROM(SELECT CId
           ,score
           ,RANK() OVER (PARTITION BY CId ORDER BY score DESC) AS rank_num
      FROM sc) AS t
WHERE rank_num <= 2; 

37、統(tǒng)計(jì)每門(mén)課程的學(xué)生選修人數(shù)(超過(guò) 5 人的課程才統(tǒng)計(jì)),要求輸出課程號(hào)和選修人數(shù),查詢(xún)結(jié)果按照人數(shù)降序,若人數(shù)相同,按照課程號(hào)升序

SELECT CId
      ,COUNT(DISTINCT SId) AS cnt
FROM sc
GROUP BY CId
HAVING cnt >= 5
ORDER BY cnt DESC
        ,CId ASC;

38、檢索至少選修兩門(mén)課程的學(xué)生學(xué)號(hào)

SELECT SId
      ,COUNT(DISTINCT CId) AS cnt
FROM sc
GROUP BY SId
HAVING cnt >= 2;

39、查詢(xún)選修了全部課程的學(xué)生信息

SELECT SId
      ,COUNT(DISTINCT CId) AS cnt
FROM sc
GROUP BY SId  # 按照sid分組之后count課程數(shù)量
HAVING cnt = (SELECT COUNT(DISTINCT Cid)
              FROM course);

40、查詢(xún)各學(xué)生的年齡,只按年份來(lái)算
解法一:用DATEDIFF函數(shù),計(jì)算出生到現(xiàn)在的時(shí)間天數(shù),然后除以365得到年份,然后用FLOOR函數(shù)向下取整得到最終的年齡

SELECT student.*
      ,FLOOR(DATEDIFF(NOW(),Sage)/365) AS "年齡"  
FROM student;

總結(jié):
DATEDIFF() 函數(shù)返回兩個(gè)日期之間的天數(shù)。
FLOOR(X)根據(jù)官方文檔的提示,floor函數(shù)返回小于等于該值的最大整數(shù).

解法二:用YEAR()函數(shù)和NOW()函數(shù)進(jìn)行相減

SELECT student.*
      ,YEAR(now())-YEAR(sage) AS age
FROM student;

41、按照出生日期來(lái)算,當(dāng)前月日 < 出生年月的月日則年齡減一
分析:比如01號(hào)同學(xué),當(dāng)前日期減去出生日期為30.9年,這取30.

# 這里 timestampdiff 會(huì)用年月日去計(jì)算 年 的相隔時(shí)間,
如果相差1.9年則為1年,所以實(shí)際上是已經(jīng)相減了的,正好用來(lái)計(jì)算生日
SELECT SId AS 學(xué)生編號(hào)
      ,Sname  AS  學(xué)生姓名
     ,TIMESTAMPDIFF(YEAR,Sage,CURDATE()) AS 學(xué)生年齡
FROM student;

總結(jié):SELECT NOW(),CURDATE(),CURTIME()的區(qū)別:


image.png

42、查詢(xún)本周過(guò)生日的學(xué)生

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

小結(jié):
YEARWEEK(date, mode)
返回年份及第幾周(0到53),mode為可選參數(shù),其中 中 0 (默認(rèn)參數(shù))表示從周天開(kāi)始,1表示周一開(kāi)始,以此類(lèi)推:

SELECT YEARWEEK("2017-06-15");   -> 201724
select WEEK('2019-07-11',1);
返回值是28
select YEARWEEK('2019-07-11',1);
返回值是201928

43、查詢(xún)下周過(guò)生日的學(xué)生

解法一:用YEARWEEK()函數(shù)

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

解法二:

SELECT sid
      ,sname
      ,EXTRACT(week FROM sage) as sweek
      ,EXTRACT(week FROM curdate()) as nweek
FROM student
WHERE  EXTRACT(week FROM sage) = EXTRACT(week FROM curdate()) + 1;

小結(jié):extract()函數(shù)的用法
EXTRACT() 函數(shù)用于返回日期/時(shí)間的單獨(dú)部分,比如年、月、日、周、小時(shí)、分鐘等等。


image.png

44、查詢(xún)本月過(guò)生日的學(xué)生
解法一:判斷月份是否相等

SELECT sid
      ,sname
      ,MONTH(sage) AS "生日"
      ,MONTH(NOW())
FROM student
WHERE  MONTH(sage) = MONTH(NOW());

解法二:EXTRACT()函數(shù)獲取月份時(shí)間,判斷是否相等

SELECT *
FROM student 
WHERE EXTRACT(MONTH FROM Sage) = EXTRACT(MONTH FROM CURDATE());

45、查詢(xún)下月過(guò)生日的學(xué)生
解法一:

SELECT sid
      ,sname
      ,MONTH(sage) AS "生日"
      ,MONTH(NOW())
FROM student
WHERE  MONTH(sage) = MONTH(NOW()) + 1;

解法二:EXTRACT()函數(shù)獲取月份時(shí)間,DATE_ADD()函數(shù)計(jì)算下月時(shí)間

SELECT *
FROM student 
WHERE EXTRACT(MONTH FROM Sage) = EXTRACT(MONTH FROM DATE_ADD(CURDATE(),INTERVAL 1 MONTH));

小結(jié):DATE_ADD()函數(shù)
定義和用法
DATE_ADD() 函數(shù)向日期添加指定的時(shí)間間隔。

語(yǔ)法
DATE_ADD(date,INTERVAL expr type)
date: 參數(shù)是合法的日期表達(dá)式
expr :參數(shù)是您希望添加的時(shí)間間隔
type :參數(shù)可以是年、月、日、周、小時(shí)、分鐘等等。


實(shí)例
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容