【sql學習】經典面試50題-PostgreSQL語句練習


 以下語句由PostgreSQL數據庫語法編寫,都全部調試過,歡迎探討~部分語句結尾未添加分號,時間關系大小寫不統一,格式不是特別整潔,強迫症求放過~

先創建四張表格 

create table student(
    s_id varchar(10),
    s_name varchar(20),
    s_age date,
    s_sex varchar(10)
);

create table course(
    c_id varchar(10),
    c_name varchar(20),
    t_id varchar(10)
);


create table teacher (
t_id varchar(10),
t_name varchar(20)
);

create table score (
    s_id varchar(10),
    c_id varchar(10),
    score integer ,
);

插入數據

insert into student (s_id, s_name, s_age, s_sex)
values  ('01' , '趙雷' , '1990-01-01' , ''),
        ('02' , '錢電' , '1990-12-21' , ''),
        ('03' , '孫風' , '1990-05-20' , ''),
        ('04' , '李雲' , '1990-08-06' , ''),
        ('05' , '周梅' , '1991-12-01' , ''),
        ('06' , '吳蘭' , '1992-03-01' , ''),
        ('07' , '鄭竹' , '1989-07-01' , ''),
        ('08' , '王菊' , '1990-01-20' , '');

insert into course (c_id, c_name, t_id)
values  ('01' , '語文' , '02'),
        ('02' , '數學' , '01'),
        ('03' , '英語' , '03');

insert into teacher (t_id, t_name)
values  ('01' , '張三'),
        ('02' , '李四'),
        ('03' , '王五');

insert into score (s_id, c_id, score)
values  ('01' , '01' , 80),
        ('01' , '02' , 90),
        ('01' , '03' , 99),
        ('02' , '01' , 70),
        ('02' , '02' , 60),
        ('02' , '03' , 80),
        ('03' , '01' , 80),
        ('03' , '02' , 80),
        ('03' , '03' , 80),
        ('04' , '01' , 50),
        ('04' , '02' , 30),
        ('04' , '03' , 20),
        ('05' , '01' , 76),
        ('05' , '02' , 87),
        ('06' , '01' , 31),
        ('06' , '03' , 34),
        ('07' , '02' , 89),
        ('07' , '03' , 98);

值得注意的是,學生的名單跟成績名單相比,學生的id是多的,也就是說8號學生是沒有成績的,另外也發現有的學生的部分課程沒有成績,這都是需要注意的,

 

1、查詢"01"課程比"02"課程成績高的學生的學號及課程分數(重點)

解題思路:

第一步:score中分別查詢出01課程的成績和02的課程的成績進行inner join,

(select s_id, score as score1 from score where c_id='01') as a inner join (select s_id, score as score2 from score where c_id='02') as b
on a.
s_id = b.s_id

第二步:以01課程成績>02課程成績進行條件篩選

    where score1 > score2 

第三步:選出需要的字段

 select  course.s_id, score1,score2

按照語句書寫順序將以上語句組織為:

select a.s_id as s_id,score1,score2 from
(select s_id, score as score1 from score where c_id='01') a
inner join
(select s_id, score as score2 from score where c_id='02') b
on a.s_id=b.s_id
where score1>score2;

 

 

 

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

select * from         
(select s_id, score as score1 from score where c_id='01') a
inner join
(select s_id, score as score2 from score where c_id='02') b
on a.s_id=b.s_id

 

 

 

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

select * from         
(select s_id, score as score1 from score where c_id='01') a
left join
(select s_id, score as score2 from score where c_id='02') b
on a.s_id=b.s_id

 

 

 

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

select * from         
(select s_id, score as score1 from score where c_id='01') a
right join
(select s_id, score as score2 from score where c_id='02') b
on a.s_id=b.s_id
where score1 is null

 

2、查詢平均成績大於60分的學生的學號和平均成績(重點)

SELECT s_id,AVG(score)
FROM score
GROUP BY s_id
having avg(score)>60

 

3、查詢所有同學的學號、姓名、選課數、總成績

簡潔的寫法
SELECT
t1.s_id,t1.s_name,count(t1.s_id),sum(t2.score) FROM student AS t1 LEFT JOIN score as t2 ON t1.s_id = t2.s_id GROUP BY t1.s_id,t1.s_name
優化版:
SELECT t1.s_id,t1.s_name,count(t1.s_id),sum(case when t2.score is NULL then 0 else t2.score end)
FROM student AS t1
LEFT JOIN
score as t2
ON t1.s_id = t2.s_id
GROUP BY t1.s_id,t1.s_name

 

 注意:這和代碼有bug。王菊同學的選課數目應該為0

 

一個冗長的答案:
SELECT
t1.s_id,t1.s_name,t2.cnt,t2.total_score FROM (SELECT s_id,s_name FROM student ) AS t1 LEFT JOIN (SELECT score.s_id,COUNT(*) AS cnt,SUM(score) AS total_score FROM score GROUP BY score.s_id) AS t2 ON t1.s_id = t2.s_id

解題思路:先將student表和score表進行聯結,這里選左聯結,確保找到所有學生的信息,然后對新表進行匯總和計算,group by中只能出現select中出現的字段喲~

第四題:查詢姓“李”的老師的個數;

SELECT count(*)
from teacher
where t_name like '李%'

 

第五題:查詢沒學過“張三”老師課的學生的學號、姓名(重點)

思路:
SELECT
DISTINCT s_id,s_name FROM student WHERE s_id NOT IN (SELECT s_id FROM score as d LEFT JOIN course AS b ON d.c_id = b.c_id LEFT JOIN teacher AS c ON b.t_id = c.t_id WHERE c.t_name = '張三')

 

 

 

常見的錯誤示范:,相當於把張三老師的記錄去掉了,實際上會選出錯誤的結果
select
distinct a.s_id,a.s_name from student as a LEFT JOIN score as d on a.s_id = d.s_id left join course as b on d.c_id = b.c_id left join teacher as c on b.t_id = c.t_id WHERE c.t_name <> '張三'

 

第六題:查詢學過“張三”老師所教的所有課的同學的學號、姓名(重點)

select distinct a.s_id,a.s_name
from student as a
LEFT JOIN score as d
on a.s_id = d.s_id
left join course as b
on d.c_id = b.c_id 
left join teacher as c
on b.t_id = c.t_id 
WHERE c.t_name = '張三'
SELECT DISTINCT s_id,s_name 
FROM student 
WHERE s_id IN
    (SELECT s_id
    FROM score as d
    LEFT JOIN course AS b ON d.c_id = b.c_id
    LEFT JOIN teacher AS c ON b.t_id = c.t_id
    WHERE c.t_name = '張三')
SELECT s_id,s_name
    from student
    where s_id in(
    SELECT s_id
    from score
    where c_id in(SELECT c_id
    from course
    where t_id in (SELECT
    t_id from teacher where t_name = '張三')))

 

7、查詢學過編號為“01”的課程並且也學過編號為“02”的課程的學生的學號、姓名(重點)

 

SELECT DISTINCT s_id,s_name 
FROM student 
WHERE s_id IN
(SELECT DISTINCT t1.s_id
    FROM
        (SELECT s_id,score
        FROM score
        WHERE c_id = '01') as t1
    JOIN
        (SELECT s_id,score
        FROM score
        WHERE c_id = '02') as t2
    ON t1.s_id = t2.s_id
)

 

 
         

 

解題思路:在這里首先是利用score表字查詢的自聯結將原來表中的列變成了行

8、查詢課程編號為“02”的總成績(不重點)

SELECT sum(score)
    from score
    where c_id = '02'

9、查詢所有課程成績小於60分的學生的學號、姓名(同第2題,要注意存在成績為null的情況,因此得采用not in)

   思路解讀:因為這樣子查詢中的結果不會包含null的id,整體-不符合要求 = 符合要求的+null
select s_id,s_name from student where s_id not in (select s_id from score GROUP BY s_id having max(score)>= 60);

 

錯誤寫法:從score中直接選id的話,這種未包含全部學生的id,成績為null的情況
    select s_id,s_name
    from student 
    where s_id in (select s_id from score GROUP BY s_id having max(score) < 60);

 

10、查詢沒有學全所有課的學生的學號、姓名(重點)

select a.s_id,a.s_name
from student as a
left join score as d
on a.s_id = d.s_id
left join course as b
on b.c_id = d.c_id
GROUP BY a.s_id,a.s_name
having count (a.s_id)<(SELECT count(distinct c_id) from course)
注意:此處不用distinct也可

 

 

 

錯誤的寫法:從score中選會漏掉選課結果為null的王菊,但是因為course表沒有跟student表直接管理,因此最好的辦法是join
select
s_id,s_name from student where s_id in (SELECT s_id,count(s_id) from score GROUP BY s_id having count(s_id) < SELECT count(*) from course))

11、查詢至少有一門課與學號為“01”的學生所學課程相同的學生的學號和姓名(重點)

inner join的效率比in高一些
一種復雜的寫法:
SELECT
t1.s_id,t1.s_name from student as t1 right join (SELECT DISTINCT s_id from score where c_id in (SELECT c_id from score where s_id = '01' )and s_id != '01') as t2 on t1.s_id = t2.s_id
注意;通過一對多方式來選擇時會出現很多重復數據,這里指,一個s_id 對應多個c_id,一對一聯結是比較好的,根據c_id來選s_id的時候,必然會發生重復,去重用distinct
SELECT
s_id,s_name from student where s_id in (SELECT DISTINCT s_id from score where c_id in (SELECT c_id from score where s_id = '01') and s_id != '01')

 

 

12.查詢和“01”號同學所學課程完全相同的其他同學的學號(重點)

 

復雜寫法
select
s_id from score where c_id = ( SELECT c_id from score where s_id = '01') and s_id != '01' GROUP BY s_id having COUNT(s_id) = (SELECT COUNT(*) from score where s_id = '01')
簡潔寫法:
SELECT
s_id from score group by s_id having (count(s_id) = (SELECT COUNT(*) from score where s_id = '01') ) and (s_id != '01')

注意:本道題因為課程一共有三項,1號同學正好有三項課程,因此指判斷課程數是否相同即可,但是如果總課程數大於3以上方法則不適用,上述方法存在bug,以下應該是正確寫法

 

注意:雙重否定的用法,not in (a,b,c) 的意思跟  in(全部集合中除去(a.b.c)的集合)的意思一樣
如果用集合的概念來解釋就是 如果兩個不同的集合a和b相加等於全集U,那么針對全集中的元素來說not in a = in b
SELECT
s_id from score where s_id not in ( select s_id from score where c_id not in ( SELECT c_id from score where s_id = '01') )and s_id != '01'GROUP BY s_id having COUNT(s_id) = (SELECT COUNT(*) from score where s_id = '01')

 

 

 

13.查詢沒學過"張三"老師講授的任一門課程的學生姓名(重點)

SELECT DISTINCT s_id,s_name 
from student 
where s_id not in  
(select s_id from score where c_id in 
(SELECT c_id from course where t_id in
(select t_id from teacher where t_name = '張三')))

 

 

14.

 

15.查詢兩門及其以上不及格課程的同學的學號,姓名及其平均成績(重點)

select a.s_id,a.s_name,t.avg_score from student as a
 INNER JOIN (SELECT s_id,AVG(score)as avg_score from score GROUP BY s_id having COUNT(*) >=2 and max(score)< 60) as t 
 on a.s_id = t.s_id

 

 

 

16.檢索"01"課程分數小於60,按分數降序排列的學生信息(和34題重復,不重點)

SELECT * from student where s_id in
(SELECT s_id from score where c_id = '01' and score < 60 ORDER BY score);

 

17.按平均成績從高到低顯示所有學生的所有課程的成績以及平均成績(重重點)

注意;這里的max起到取出數據的作用,沒有實際的max功能,分別進行操作的時候,可以用case when
SELECT
s_id, max (case when c_id = '02' then score else null end) as "數學", max (case when c_id = '01' then score else null end) as "語文", max (case when c_id = '03' then score else null end) as "英語", AVG(score) from score GROUP BY s_id ORDER BY AVG(score) desc

 

 
         

 

 

錯誤示范:這種形式不是最優解
SELECT
a.s_id,d.score,AVG(d.score) FROM student as a LEFT JOIN score as d on a.s_id = d.s_id GROUP BY a.s_id,d.score ORDER BY AVG(d.score) desc

 

18.查詢各科成績最高分、最低分和平均分:以如下形式顯示:課程ID,課程name,最高分,最低分,平均分,及格率,中等率,優良率,優秀率

SELECT c_id,max (score),min (score),avg(case when score is not null then score else 0 end),
AVG(case when score >= 70 and score < 80 then 1 else 0 end) as middle,
AVG(case when score >= 80 and score < 90 then 1 else 0 end) as good,
AVG(case when score >= 90  then 1 else 0 end) as great
from score
GROUP BY c_id;

 

 

 

19、按各科成績進行排序,並顯示排名(重點dens_rank()over(order by 列)

SELECT s_id,c_id,score,
DENSE_RANK()OVER(PARTITION BY c_id ORDER BY score desc) AS ranking
from score

 

 

窗口函數的用法看這里:看這里

 

 

 

 

 

 

 

 

 

20、查詢學生的總成績並進行排名(不重點)

SELECT s_id,sum(score)
from score 
group by s_id
order by sum(score) desc

 

21、查詢不同老師所教不同課程平均分從高到低顯示(不重點)

SELECT c.t_name,d.c_id,avg(d.score)
from score as d
inner join course as b
on d.c_id = b.c_id
inner join teacher as c
on c.t_id = b.t_id
group by d.c_id,c.t_name
order by avg(d.score)desc

22、查詢所有課程的成績第2名到第3名的學生信息及該課程成績(重要 25類似)

SELECT c_id,s_name,score
from  student as a
INNER join (SELECT *,
DENSE_RANK()OVER(PARTITION BY c_id ORDER BY score desc) AS ranking
from score) as t
on a.s_id = t.s_id
where ranking = 2 or ranking = 3
ORDER BY c_id,score des

 

 

 

 

23、使用分段[100-85],[85-70],[70-60],[<60]來統計各科成績,分別統計各分數段人數:課程ID和課程名稱(重點和18題類似)

select co.c_id ,c_name,
sum (case when score < 60 then 1 else 0 end ) as D,
sum (case when score >= 60 and score < 70 then 1 else 0 end ) as C,
sum (case when score >= 70 and score < 85 then 1 else 0 end ) as B,
sum (case when score >= 85 and score < 100 then 1 else 0 end ) as A
from score as sc
left join course as co
on co.c_id = sc.c_id
GROUP BY co.c_id ,c_name

 

24、查詢學生平均成績及其名次(同19題,重點)

注意在:DENSE_RANK()OVER( ORDER BY avg_score desc) AS ranking中對成績按照desc進行排序
在結尾對ranking升序
SELECT * ,
DENSE_RANK()OVER( ORDER BY avg_score desc) AS ranking
from (SELECT s_id,AVG(case when score is not null then score else 0 end) as avg_score
FROM score 
GROUP BY s_id) as t
ORDER BY ranking 

考慮成績並列可以用dense_rank(),不考慮成績並列可以用row_number()

25、查詢各科成績前三名的記錄(不考慮成績並列情況)(重點 與22題類似)

SELECT c_id,s_name,score
from  student as a
INNER join (SELECT *,
ROW_NUMBER()OVER(PARTITION BY c_id ORDER BY score desc) AS ranking
from score) as t
on a.s_id = t.s_id
where ranking = 1 or ranking = 2 or ranking = 3
ORDER BY c_id,score desc

如果需要知道課程名稱,可以繼續join一個course表

 

26、查詢每門課程被選修的學生數(不重點)

SELECT c_id,count(*)
from score
GROUP BY c_id

27、 查詢出只有兩門課程的全部學生的學號和姓名(不重點)

SELECT s_id,s_name
from student
where s_id in 
(SELECT s_id 
from score
GROUP BY s_id
having count(*) = 2)

28、查詢男生、女生人數(不重點)

select s_sex,count(s_sex)
from student
group by s_sex;

 

29、查詢名字中含有"風"字的學生信息(不重點)

select *
from student
where s_name like '%風'

 

30、

31、查詢1990年出生的學生名單(重點year)

select *
from student
where s_age like '1900%';

 

32、查詢平均成績大於等於85的所有學生的學號、姓名和平均成績(不重要)

select a.s_id,a.s_name,avg(case when d.score is not null then d.score else 0 end)
from student as a
left join score as d
on a.s_id = d.s_id
group by a.s_id,a.s_name
having avg(d.score) > 85;

 

33、查詢每門課程的平均成績,結果按平均成績升序排序,平均成績相同時,按課程號降序排列(不重要)

select c_id ,avg (score)
from score
group by c_id 
order by avg (score) asc,c_id desc

 

34、查詢課程名稱為"數學",且分數低於60的學生姓名和分數(不重點)

select a.s_name,d.score,d.c_id
from student as a 
inner join score as d
on a.s_id = d.s_id
where c_id in (select c_id from course where c_name = '數學') and d.score < 60

35、查詢所有學生的課程及分數情況(重點)

SELECT a.s_id,a.s_name,
sum(case when d.c_id = '01' then d.score else null end ) as sub_01,
sum(case when d.c_id = '02' then d.score else null end ) as sub_02,
sum(case when d.c_id = '03' then d.score else null end ) as sub_03
from student as a 
left join score as d
on a.s_id = d.s_id
group by a.s_id,a.s_name
 
錯誤滴答案:
SELECT
a.s_id ,a.s_name,d.score from student as a left join score as d on a.s_id = d.s_id

 

36、查詢任何一門課程成績在70分以上的姓名、課程名稱和分數(重點)

 

正常答案:
select
a.s_name,b.c_name,d.score from student as a inner join score as d on a.s_id = d.s_id inner join course as b on d.c_id = b.c_id where d.score > 70

 

一個比較復雜又沒必要的答案:
select
a.s_name,b.c_name,d.score from student as a inner join score as d on a.s_id = d.s_id inner join course as b on d.c_id = b.c_id GROUP BY a.s_name,b.c_name,d.score having MIN(d.score) > 70 ORDER BY a.s_name;

 

37、查詢不及格的課程並按課程號從大到小排列(不重點)

select d.c_id, b.c_name ,score
from score as d
inner join course as b
on d.c_id = b.c_id
where score < 60
order by d.c_id DESC

 

38、查詢課程編號為03且課程成績在80分以上的學生的學號和姓名(不重要)

select a.s_id,a.s_name
from student as a
left join score as d
on a.s_id = d.s_id
where c_id = '03' and score > 80;

39、求每門課程的學生人數(不重要)

select c_id , count(*)
from score
GROUP BY c_id

40、查詢選修“張三”老師所授課程的學生中成績最高的學生姓名及其成績(重要limit)

解法1:
SELECT
s_name,score FROM student as a inner join score as d on a.s_id = d.s_id where c_id in (select c_id from course where t_id in(select t_id from teacher where t_name = '張三')) ORDER BY score desc LIMIT 1

 

 
         

 

 
解法2:
SELECT
s_name,score FROM student as a inner join score as d on a.s_id = d.s_id inner join course as b on d.c_id = b.c_id inner join teacher as c on b.t_id = c.t_id where t_name = '張三' ORDER BY score desc LIMIT 1
解法3:
select s_name,score
from 
(SELECT s_name,score,dENSE_RANK()OVER(ORDER BY score desc) AS ranking
FROM student as a
inner join score as d
on a.s_id = d.s_id
inner join course as b
on d.c_id = b.c_id
inner join teacher as c
on b.t_id = c.t_id
where t_name = '張三' 
ORDER BY score desc) as t
where ranking = 1

 

41、查詢不同課程成績相同的學生的學生編號、課程編號、學生成績 (重點)

網上答案,我認為這個答案也不夠嚴謹,因為學生可能兩個成績相同也可能三個成績相同,
select
s1.s_id,s1.c_id,s1.score,s2.score,s3.score from (select s_id,c_id,score from score where c_id = '01' ) as s1 inner join (select s_id,c_id,score from score where c_id = '02' ) as s2 on s1.s_id = s2.s_id inner join (select s_id,c_id,score from score where c_id = '02' ) as s3 on s2.s_id = s3.s_id where s1.score = s2.score and s2.score = s3.score
我認為我這個是正確答案
select
s_id,c_id,score from score where s_id not in (select s_id from score group by s_id having max(score)!= min(score))

 

 

42、查詢每門功成績最好的前兩名(同22和25題)

SELECT c_id,s_name,score
from  student as a
INNER join (SELECT *,
DENSE_RANK()OVER(PARTITION BY c_id ORDER BY score desc) AS ranking
from score) as t
on a.s_id = t.s_id
where ranking = 1 or ranking = 2
ORDER BY c_id,score desc

 

43、統計每門課程的學生選修人數(超過5人的課程才統計)。要求輸出課程號和選修人數,查詢結果按人數降序排列,若人數相同,按課程號升序排列(不重要)

select c_id,COUNT(*)
from score 
GROUP BY c_id
having COUNT(*) > 5
order by COUNT(*) desc ,c_id asc

44、檢索至少選修兩門課程的學生學號(不重要)

select s_id,COUNT(*)
from score 
GROUP BY s_id
having COUNT(*) >= 2

45、查詢選修了全部課程的學生信息(重點)

select *
from student 
where s_id in (select s_id from score group by s_id having count(DISTINCT c_id) = (select count(*) from course))

47、查詢沒學過“張三”老師講授的任一門課程的學生姓名

select s_id,s_name
from student
where s_id not in
(SELECT s_id
from score
where c_id in (select c_id from course where t_id in (select t_id from teacher where t_name = '張三')))

48、查詢兩門以上不及格課程的同學的學號及其平均成績

select s_id,avg(score)
from score
group by s_id
having COUNT(s_id) >= 2 and max(score) < 60
select s_id,avg(score)
from score
where score < 60
group by s_id
having COUNT(s_id) >= 2 

49、查詢各學生的年齡(精確到月份)

備注:年份轉換成月份,比如結果是1.9,ditediff 最后取1年

SELECT s_id,s_age,
extract(YEAR from age(current_date,s_age))
from student

 

 

時間函數用法看這里:https://www.yiibai.com/html/postgresql/2013/080783.html

50、查詢本月過生日的學生

SELECT *
from student
where EXTRACT(month from s_age) = EXTRACT(month from CURRENT_DATE)

 

 

50.1、查詢下月過生日的學生

注意where子句中,兩個select 子句部分都要各自加括號,這樣括號內先計算得出一個具體的數值
SELECT
* from student where (SELECT extract (month from s_age)) = (SELECT extract(month from CURRENT_DATE) +1)
 

50.2、查詢本周過生日的學生

50.3、查詢下周過生日的學生

因為postgresql中沒有week函數,暫時還不知道怎么寫

未完待續~因為還不太會優化sq代碼,日后再標注出比較好的解答

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM