近期在準備面試,發現自己寫sql語句的能力是真滴差,就在網上找了這個MYSQL經典習題來練習,寫了該博客來記錄自己的做題過程還有自己的一些分析過程
讓我們開始吧…
持續練習更新中…
第一次寫博客,要是有說得不對的地方,還請網友指正~
數據庫表和測試數據
可以自己建一個數據庫將這些數據導入~
#–1.學生表
#Student(s_id,s_name,s_birth,s_sex) –學生編號,學生姓名, 出生年月,學生性別
CREATE TABLE `Student` (
`s_id` VARCHAR(20),
s_name VARCHAR(20) NOT NULL DEFAULT '',
s_brith VARCHAR(20) NOT NULL DEFAULT '',
s_sex VARCHAR(10) NOT NULL DEFAULT '',
PRIMARY KEY(s_id)
);
#–2.課程表
#Course(c_id,c_name,t_id) – –課程編號, 課程名稱, 教師編號
create table Course(
c_id varchar(20),
c_name VARCHAR(20) not null DEFAULT '',
t_id VARCHAR(20) NOT NULL,
PRIMARY KEY(c_id)
);
#3.教師表
#Teacher(t_id,t_name) –教師編號,教師姓名
CREATE TABLE Teacher(
t_id VARCHAR(20),
t_name VARCHAR(20) NOT NULL DEFAULT '',
PRIMARY KEY(t_id)
);
#4.成績表
#Score(s_id,c_id,s_score) –學生編號,課程編號,分數
Create table Score(
s_id VARCHAR(20),
c_id VARCHAR(20) not null default '',
s_score INT(3),
primary key(`s_id`,`c_id`)
);
#--插入學生表測試數據
#('01' , '趙雷' , '1990-01-01' , '男')
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' , '女');
#--課程表測試數據
insert into Course values('01' , '語文' , '02');
insert into Course values('02' , '數學' , '01');
insert into Course values('03' , '英語' , '03');
#--教師表測試數據
insert into Teacher values('01' , '張三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');
#--成績表測試數據
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' , 80);
insert into Score values('03' , '01' , 80);
insert into Score values('03' , '02' , 80);
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);
1查詢"01"課程比"02"課程成績高的學生的信息及課程分數
拿到這道題的第一反應就是要分別取出01課程和02課程的成績然後去進行比較,然後再取成績的時候就做了出來(其實在看題目的時候也瞄到了答案,受到了啓發)
我在做這些多表操作的時候,是喜歡先一個表一個表操作,然後再慢慢拼接起來,我覺得這樣做出錯的概率會比較低
-- 先查出所有學生信息
SELECT s.s_id,s.s_name,s.s_brith,s.s_sex FROM student s
-- 湊出答案
SELECT s.*,b.s_score as score2 ,c.s_score as score3 FROM student s
LEFT JOIN score b ON s.s_id = b.s_id AND b.c_id = 01
LEFT JOIN score c ON s.s_id = c.s_id AND c.c_id = 02
WHERE b.s_score > c.s_score
2、查詢"01"課程比"02"課程成績低的學生的信息及課程分數
直接粘貼第一題改個符號就好了…
3查詢平均成績大於等於60分的同學的學生編號和學生姓名和平均成績
在這裏參考答案與我不同的地方是他用了一個ROUND(X,D)函數,這個以前沒用過,不過看結果也看得出,他的作用是按照D限定的位數進行四捨五入的運算
-- 我的答案
SELECT s.s_id,s.s_name,AVG(sc.s_score),2 AVGScore FROM student s
LEFT JOIN score sc ON s.s_id = sc.s_id
GROUP BY s.s_id HAVING ROUND(AVG(sc.s_score),2)>60
-- 參考答案
select b.s_id,b.s_name,ROUND(AVG(a.s_score),2) as avg_score from
student b
join score a on b.s_id = a.s_id
GROUP BY b.s_id,b.s_name HAVING ROUND(AVG(a.s_score),2)>=60;
4、查詢平均成績小於60分的同學的學生編號和學生姓名和平均成績
一開始我天真的以爲第3題copy下來改個符號就行了
SELECT s.s_id,s.s_name,AVG(sc.s_score),2 AVGScore FROM student s
LEFT JOIN score sc ON s.s_id = sc.s_id
GROUP BY s.s_id HAVING ROUND(AVG(sc.s_score),2)<60
看了下運行結果,和答案相比少了一個人,原來我是將平均成績爲null的人過濾掉了
-- 答案一,以爲將有null值的加進來就行了
SELECT s.s_id,s.s_name,ROUND(AVG(sc.s_score),2) AVGScore
FROM student s
LEFT JOIN score sc ON s.s_id=sc.s_id
GROUP BY s.s_id HAVING ROUND(AVG(sc.s_score),2)<60 OR AVG(sc.s_score) is NULL
-- 這樣的話就是在成績那一欄會出現一個null值,很不合理,於是就有了以下改進
-- 我的答案:
SELECT s.s_id,s.s_name,IFNULL(ROUND(AVG(sc.s_score),2),0) AVGScore
FROM student s
LEFT JOIN score sc ON s.s_id=sc.s_id
GROUP BY s.s_id HAVING ROUND(AVG(sc.s_score),2)<60 OR AVG(sc.s_score) is NULL
-- 網上參考答案
SELECT stu.`s_id`,stu.`s_name`,AVG(sc.s_score)
FROM student stu LEFT JOIN score sc ON stu.`s_id`= sc.s_id
GROUP BY stu.`s_id`
HAVING AVG(sc.s_score)<60
UNION
SELECT stu.`s_id`,stu.`s_name`,0 AS avg_score
FROM student stu
WHERE stu.`s_id` NOT IN (
-- 成績表裏面沒有成績的人,用0去替代他
SELECT DISTINCT s_id FROM score
)
我這裏和答案不同的是,我使用了一個 IFNULL(X,D) 函數,X表示的是列名,D表示的是用去替代null的值
而答案是去查找成績表裏面沒有的人,然後用0去替代,使用的UNION關聯查詢將兩次結果拼起來
5查詢所有同學的學生編號、學生姓名、選課總數、所有課程的總成績
一開始看到這道題的時候,我是有點蒙的,因爲沒有選課表,何來選課總數,後來仔細看成績表,就想着應該是有成績的就是選了的課吧,這道題只能這樣做了
-- 我的答案
SELECT s.s_id,s.s_name,COUNT(sc.c_id) 選課總數,IFNULL(SUM(sc.s_score),0) 總成績 FROM student s
LEFT JOIN score sc ON s.s_id=sc.s_id
GROUP BY s.s_id
其實在這裏我有個疑問,以下爲答案:答案的GROUP BY 後面是有兩個條件的,
而我覺得一個參數其實也可,不知道會不會有什麼影響,我目前是感受不出來
-- 參考答案
select a.s_id,a.s_name,count(b.c_id) as sum_course,sum(b.s_score) as sum_score from
student a
left join score b on a.s_id=b.s_id
GROUP BY a.s_id,a.s_name;
6、查詢"李"姓老師的數量
這道題很簡單直接給結果了,我的答案和參考答案的區別是我用了COUNT(*)
答案使用了count(t_id),但是在做下面13題的時候,查了下資料:https://www.cnblogs.com/welearn/p/11357470.html ,參考這篇博客的說法,似乎使用的COUNT(*)的效率會更高。
-- 我的答案
SELECT COUNT(*) FROM teacher WHERE t_name LIKE '李%'
--參考答案
select count(t_id) from teacher where t_name like '李%';
7、查詢學過"張三"老師授課的同學的信息
分析:張三老師在課程表對應的課程id爲02,於是就是在成績表中查出02課程有成績的同學信息即可
這裏我感覺我的答案會比參考答案好一點有木有(我那樣寫應該沒bug吧)
-- 我的答案(上面剛說了不用 * 現在又用了哈哈)
SELECT * FROM student WHERE s_id IN
(SELECT s_id FROM score WHERE c_id=02)
-- 參考答案
select a.* from student a
join score b on a.s_id=b.s_id where b.c_id in(
select c_id from course where t_id =(
select t_id from teacher where t_name = '張三'));
8、查詢沒學過"張三"老師授課的同學的信息
分析:學過張三老師剩下的人就是沒有學過的了,所以將IN 改爲NOT IN即可
-- 我的答案
SELECT * FROM student WHERE s_id NOT IN (SELECT s_id FROM score WHERE c_id=02)
-- 參考答案
select * from
student c
where c.s_id not in(
select a.s_id from student a join score b on a.s_id=b.s_id where b.c_id in(
select c_id from course where t_id =(
select t_id from teacher where t_name = '張三')));
9、查詢學過編號爲"01"並且也學過編號爲"02"的課程的同學的信息
分析:這道題我的AND之間連接了兩次select語句,很明顯沒有參考答案的效率高,看了參考答案才恍然大悟原來還可以這樣操作…
因爲這裏要用到兩次score表的條件,如果只用一次表的話,那麼AND連起來的就說明是要在同一列的才能查得出,所以必須使用別名來查詢
-- 我的答案
SELECT* FROM student WHERE student.s_id IN
(SELECT s_id FROM score WHERE c_id='01' ) AND
student.s_id IN(SELECT s_id FROM score WHERE c_id='02')
--參考答案
select a.* 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';
10、查詢學過編號爲"01"但是沒有學過編號爲"02"的課程的同學的信息
分析:這裏的我的答案和參考答案是一樣的,和答案一樣都是用的我第九題的答案
--我的答案
SELECT * FROM student WHERE s_id IN
(SELECT s_id FROM score WHERE c_id='01' )
AND s_id NOT IN(SELECT s_id FROM score WHERE c_id='02')
11、查詢沒有學全所有課程的同學的信息
分析:這道題一開始的想法是使用了第九題參考答案的方法去查出學了全部課程的學生id,然後再套一個not in 去查詢的,可是查出來的答案卻少了一個值,不知道是什麼原因
SELECT * FROM student WHERE s_id NOT IN
(SELECT t.s_id FROM student t,score a,score b,score c WHERE
t.s_id = a.s_id AND t.s_id = b.s_id AND t.s_id = b.s_id AND a.c_id='01' AND b.c_id='02' AND c.c_id='03')
s_id爲5的學生信息沒有顯示出來
score表:
結果:
於是看了參考答案:
SELECT * FROM student WHERE s_id NOT IN
(SELECT t.s_id FROM student t,score a,score b,score c WHERE t.s_id = a.s_id AND t.s_id = b.s_id AND t.s_id = b.s_id AND a.c_id='01' AND b.c_id='02' AND c.c_id='03')
答案的結果是(如果說沒有學全是至少有學一門課程的話,那就是這個答案了!);
------10月10日補充更新
經過了一晚上的時間,重新又來查看那個sql語句,發現原來是我番了一個很低級的錯誤…
這裏寫重複了,,所以大家寫sql語句的時候一定要細心啊…
經過糾正:
SELECT * FROM student WHERE s_id NOT IN
(SELECT t.s_id FROM student t,score a,score b,score c WHERE t.s_id = a.s_id AND t.s_id = b.s_id AND t.s_id = c.s_id AND a.c_id='01' AND b.c_id='02' AND c.c_id='03')
得到了我先要的答案了:
第11題總結:
- 我覺得這道題有兩個理解,一是全部沒有學的頁查出來,而是像參考答案那樣查出至少有學過一門課的學生
- 再寫比較長重複性比較高的sql語句的時候一定要細心,認真檢查,避免寫錯…
12、查詢至少有一門課與學號爲"01"的同學所學相同的同學的信息
這道題比較簡單,我就直接上我的拼湊sql過程了
我的答案和參考答案不同的地方是,我過濾了學號爲01的同學,而答案是沒有過濾的,還有答案用了distinct關鍵字過濾了重複的id這樣效率會更高,比我的sql更好
# 我的答案
-- 先查出學號爲01的所有課程cid
SELECT c.c_id FROM score c WHERE s_id='01'
-- 再根據課程id查出學生id,注意這裏要取出id爲01的學生
SELECT b.s_id FROM score b WHERE b.c_id IN (SELECT c.c_id FROM score c WHERE s_id='01') AND b.s_id != '01'
-- 就可以拼湊出答案了
SELECT s.* FROM student s WHERE s.s_id IN (SELECT b.s_id FROM score b WHERE b.c_id IN (SELECT c.c_id FROM score c WHERE s_id='01') AND b.s_id != '01')
#網上參考答案
select * from student where s_id in(
select distinct a.s_id from score a where a.c_id in(select a.c_id from score a where a.s_id='01')
);
13、查詢和"01"號的同學學習的課程完全相同的其他同學的信息
- 這道題我是沒有做出來的,看了答案,個人覺得這個答案並不合理!
- 答案的解發是去統計id爲01學生所學的課程數量,然後根據01學生所學的課程id去查出學生id,根據學生id去統計id數量,如果學生id的數量與課程id數量相等,就說明學的是完全一樣的。
- 因爲這道題剛好是巧合,才能做出正確的答案,因爲01同學剛好將所有的課程都學了,所以我們只需統計數量即可,加入01同學沒有將所有的課程都學了的話,那麼將不能出現正確的結果
-- 參考答案
select a.* from student a where a.s_id in(
select distinct s_id from score where s_id!='01' and c_id in(select c_id from score where s_id='01')
group by s_id
having count(1)=(select count(1) from score where s_id='01'));
14、查詢沒學過"張三"老師講授的任一門課程的學生姓名
分析:這道題比較簡單,只需查出張三老師的所有課程id,然後再去查出有學過的人的學生id,最後用ont in 即可查出所欲沒學過的學生信息。
-- 1.先查出張三老師的所有課程
SELECT co.c_id FROM course co WHERE co.t_id IN
(SELECT t.t_id FROM teacher t WHERE t.t_name = '張三')
-- 2.根據課程id去查出學生id 沒有在裏面的就是沒上過課的
SELECT sc.s_id FROM score sc WHERE sc.c_id IN
(SELECT co.c_id FROM course co WHERE co.t_id IN
(SELECT t.t_id FROM teacher t WHERE t.t_name = '張三'))
-- 3.即可拼湊出答案了
-- 我的答案:
SELECT s.s_name FROM student s WHERE s.s_id NOT IN
(SELECT sc.s_id FROM score sc WHERE sc.c_id IN
(SELECT co.c_id FROM course co WHERE co.t_id IN
(SELECT t.t_id FROM teacher t WHERE t.t_name = '張三')))
-- 參考答案:答案下面的查詢用了 = 號,這裏我認爲是不合理的,這樣的話就默認了張三老師只能有一門課程了,萬一有兩門的話,那就查不出了!
select a.s_name from student a where a.s_id not in (
select s_id from score where c_id =
(select c_id from course where t_id =(
select t_id from teacher where t_name = '張三'))
group by s_id);
15、查詢兩門及其以上不及格課程的同學的學號,姓名及其平均成績
分析:這道題可以根據成績表先求出兩門及其以上不及格課程的同學的學號,平均成績,再拼名字
-- 先查出兩門成績不合格的,根據分數條件查出兩門及其以上不合格成績人的成績和學生id
SELECT a.s_id,COUNT(a.s_id), AVG(a.s_score) FROM
score a WHERE a.s_score<60 GROUP BY a.s_id HAVING COUNT(a.s_id)>=2
這樣就可以查出學生的id,不及格課程的數量和平均成績,接下來只需將這些列拼接起立,再根據裏面的s_id即可求出對應學生的名字:
-- 我的答案
SELECT s.s_id,s.s_name,AVG(a.s_score) FROM student s LEFT JOIN
score a ON s.s_id = a.s_id WHERE s.s_id IN
(SELECT a.s_id FROM score a WHERE a.s_score<60 GROUP BY a.s_id
HAVING COUNT(a.s_id)>=2) GROUP BY s.s_id
-- 參考答案
select a.s_id,a.s_name,ROUND(AVG(b.s_score)) from
student a
left join score b on a.s_id = b.s_id
where a.s_id in(
select s_id from score where s_score<60 GROUP BY s_id having count(1)>=2)
GROUP BY a.s_id,a.s_name
由於字數太多了,決定分開幾篇博客寫,後續題目請查看:
sql面試經典50題練習筆記一(本篇):https://blog.csdn.net/maggrect/article/details/102461646
sql面試經典50題練習筆記二:傳送門https://blog.csdn.net/maggrect/article/details/102482119
題目來源:
首先是通過百度,看到了這位博主的博客:傳送門https://blog.csdn.net/MACHENIC/article/details/100602446 通過他的博客找到了經典sql50題:傳送門https://www.cnblogs.com/Diyo/p/11424844.html,於是纔有了這篇練習筆記