零基础入门 PostgreSQL 练习题:20 道 SQL 从入门到进阶 本文精选 20 道 SQL 练习题从基础单表查询到高阶开窗函数与复杂聚合覆盖日常开发与面试中 90% 的常见场景。先列出全部题目再附上完整可运行的 PostgreSQL 标准答案方便读者先思考后对照。前置基础你需要准备好以下三张表标准学生-课程-成绩模型-- 学生表 CREATE TABLE student ( sid INT PRIMARY KEY, sname VARCHAR(50), ssex CHAR(2), sdept VARCHAR(50) ); -- 课程表 CREATE TABLE course ( cid INT PRIMARY KEY, cname VARCHAR(100), cteacher VARCHAR(50), ccredit NUMERIC(3,1) ); -- 成绩表 CREATE TABLE sc ( sid INT REFERENCES student(sid), cid INT REFERENCES course(cid), score NUMERIC(5,2), PRIMARY KEY (sid, cid) );请自行插入测试数据不少于 10 条学生、5 门课程、20 条成绩记录以便验证答案。第一部分题目共 20 题基础入门1~7 题单表查询、条件、排序、聚合基础查询所有学生的全部信息查询计算机学院所有男生的姓名、学号查询学分大于等于 3.5 的所有课程名称、授课老师查询《数据库原理》这门课的所有学生成绩按分数降序排序统计一共有多少名学生、多少门课程查询成绩不及格60的学生学号、课程号、分数统计每一门课程的平均分、最高分、最低分中等进阶8~14 题多表联查、子查询、分组过滤、INNER/LEFT JOIN查询每个学生姓名、所选全部课程名、对应分数三表联查查询张教授授课的所有学生姓名与对应分数查询至少选了 2 门课的学生姓名、选课总门数、平均分查询没有选《数据库原理》的所有学生姓名查询每一位学生选课的总学分只显示总学分 ≥ 7 的学生查询平均分高于 85 分的课程名称、平均分查询所有选了 Java 开发但没选操作系统的学生姓名高阶难题15~20 题关联子查询、开窗函数、存在判断、TopN、复杂聚合查询每门课分数排名前 2 的学生姓名、分数开窗函数 rank查询所有各科分数都高于 80 分的学生姓名查询和李四同院系的全部学生子查询 in找出平均分最高的学生输出姓名、平均分查询存在不及格科目的学生姓名、不及格课程名、分数统计每个院系所有学生的平均分并按平均分从高到低排序第二部分答案共 20 题基础入门1~7 题第 1 题查询所有学生的全部信息SELECT * FROM student;第 2 题查询计算机学院所有男生的姓名、学号SELECT sid, sname FROM student WHERE sdept 计算机学院 AND ssex 男;第 3 题查询学分大于等于 3.5 的所有课程名称、授课老师SELECT cname, cteacher FROM course WHERE ccredit 3.5;第 4 题查询《数据库原理》这门课的所有学生成绩按分数降序排序SELECT s.sname, sc.score FROM student s JOIN sc ON s.sid sc.sid JOIN course c ON sc.cid c.cid WHERE c.cname 数据库原理 ORDER BY sc.score DESC;第 5 题统计一共有多少名学生、多少门课程-- 方法一分开查询 SELECT COUNT(sid) AS student_total FROM student; SELECT COUNT(cid) AS course_total FROM course; -- 方法二合并为一行输出 SELECT (SELECT COUNT(sid) FROM student) AS student_total, (SELECT COUNT(cid) FROM course) AS course_total;第 6 题查询成绩不及格60的学生学号、课程号、分数SELECT sid, cid, score FROM sc WHERE score 60;第 7 题统计每一门课程的平均分、最高分、最低分SELECT c.cname, ROUND(AVG(sc.score), 2) AS avg_score, MAX(sc.score) AS max_score, MIN(sc.score) AS min_score FROM course c JOIN sc ON c.cid sc.cid GROUP BY c.cid, c.cname;中等进阶8~14 题第 8 题查询每个学生姓名、所选全部课程名、对应分数三表联查SELECT s.sname, c.cname, sc.score FROM student s JOIN sc ON s.sid sc.sid JOIN course c ON sc.cid c.cid ORDER BY s.sid;第 9 题查询张教授授课的所有学生姓名与对应分数SELECT DISTINCT s.sname, sc.score FROM student s JOIN sc ON s.sid sc.sid JOIN course c ON sc.cid c.cid WHERE c.cteacher 张教授;第 10 题查询至少选了 2 门课的学生姓名、选课总门数、平均分SELECT s.sname, COUNT(sc.cid) AS course_count, ROUND(AVG(sc.score), 2) AS avg_score FROM student s JOIN sc ON s.sid sc.sid GROUP BY s.sid, s.sname HAVING COUNT(sc.cid) 2;第 11 题查询没有选《数据库原理》的所有学生姓名SELECT DISTINCT s.sname FROM student s WHERE s.sid NOT IN ( SELECT sc.sid FROM sc JOIN course c ON sc.cid c.cid WHERE c.cname 数据库原理 );第 12 题查询每一位学生选课的总学分只显示总学分 ≥ 7 的学生SELECT s.sname, SUM(c.ccredit) AS total_credit FROM student s JOIN sc ON s.sid sc.sid JOIN course c ON sc.cid c.cid GROUP BY s.sid, s.sname HAVING SUM(c.ccredit) 7;第 13 题查询平均分高于 85 分的课程名称、平均分SELECT c.cname, ROUND(AVG(sc.score), 2) AS avg_score FROM course c JOIN sc ON c.cid sc.cid GROUP BY c.cid, c.cname HAVING AVG(sc.score) 85;第 14 题查询所有选了 Java 开发但没选操作系统的学生姓名SELECT DISTINCT s.sname FROM student s JOIN sc sc1 ON s.sid sc1.sid JOIN course c1 ON sc1.cid c1.cid WHERE c1.cname Java开发 AND s.sid NOT IN ( SELECT sc2.sid FROM sc sc2 JOIN course c2 ON sc2.cid c2.cid WHERE c2.cname 操作系统 );高阶难题15~20 题第 15 题查询每门课分数排名前 2 的学生姓名、分数开窗函数 rankWITH course_rank AS ( SELECT c.cname, s.sname, sc.score, RANK() OVER (PARTITION BY c.cid ORDER BY sc.score DESC) AS rk FROM student s JOIN sc ON s.sid sc.sid JOIN course c ON sc.cid c.cid ) SELECT cname, sname, score FROM course_rank WHERE rk 2;第 16 题查询所有各科分数都高于 80 分的学生姓名SELECT DISTINCT s.sname FROM student s WHERE NOT EXISTS ( SELECT 1 FROM sc WHERE sc.sid s.sid AND sc.score 80 );第 17 题查询和李四同院系的全部学生子查询 inSELECT sname FROM student WHERE sdept ( SELECT sdept FROM student WHERE sname 李四 );第 18 题找出平均分最高的学生输出姓名、平均分WITH student_avg AS ( SELECT s.sname, ROUND(AVG(sc.score), 2) AS avg_score FROM student s JOIN sc ON s.sid sc.sid GROUP BY s.sid, s.sname ) SELECT sname, avg_score FROM student_avg ORDER BY avg_score DESC LIMIT 1;第 19 题查询存在不及格科目的学生姓名、不及格课程名、分数SELECT DISTINCT s.sname, c.cname, sc.score FROM student s JOIN sc ON s.sid sc.sid JOIN course c ON sc.cid c.cid WHERE sc.score 60;第 20 题统计每个院系所有学生的平均分并按平均分从高到低排序SELECT s.sdept, ROUND(AVG(sc.score), 2) AS dept_avg_score FROM student s JOIN sc ON s.sid sc.sid GROUP BY s.sdept ORDER BY dept_avg_score DESC;避坑指南常见错误原因正确做法SELECT * FROM sc GROUP BY cid报错SELECT *包含了非分组列sid、score只选择分组列和聚合函数HAVING score 80报错score不是聚合函数也不是分组列改用WHERE或HAVING AVG(score) 80NOT IN (SELECT ...)结果为空子查询中有 NULL 值使用NOT EXISTS或加WHERE ... IS NOT NULL开窗函数RANK()并列第一名后第二名序号为 3RANK()的特性并列占用下一个名次如需连续排名用DENSE_RANK()核心口诀先单表后多表先筛选后聚合先分组再过滤先 CTE 再开窗。