文章目录
- 数据库系统概论第6版:第二章关系代数与第三章SQL期末重点整理
-
- 一、三张表结构
-
- [1. 课程表 Course](#1. 课程表 Course)
- [2. 选课表 SC](#2. 选课表 SC)
- [3. 学生表 Student](#3. 学生表 Student)
- 二、第二章:关系代数重点
-
- [1. 选择运算 σ:选行](#1. 选择运算 σ:选行)
-
- [例 1:查询信息安全专业的学生](#例 1:查询信息安全专业的学生)
- [例 2:查询男生信息](#例 2:查询男生信息)
- [例 3:查询成绩大于等于 90 分的选课记录](#例 3:查询成绩大于等于 90 分的选课记录)
- [2. 投影运算 π:选列](#2. 投影运算 π:选列)
-
- [例 1:查询所有学生的学号和姓名](#例 1:查询所有学生的学号和姓名)
- [例 2:查询所有课程的课程号、课程名和学分](#例 2:查询所有课程的课程号、课程名和学分)
- [例 3:查询所有开课学期](#例 3:查询所有开课学期)
- [3. 选择 + 投影组合](#3. 选择 + 投影组合)
-
- [例 1:查询信息安全专业学生的姓名](#例 1:查询信息安全专业学生的姓名)
- [例 2:查询 81001 课程的选课学生学号和成绩](#例 2:查询 81001 课程的选课学生学号和成绩)
- [例 3:查询学分为 4 的课程名](#例 3:查询学分为 4 的课程名)
- [4. 笛卡尔积 ×](#4. 笛卡尔积 ×)
- [5. 连接运算 ⋈](#5. 连接运算 ⋈)
-
- [例 1:查询学生姓名、课程号和成绩](#例 1:查询学生姓名、课程号和成绩)
- [例 2:查询学生姓名、课程名和成绩](#例 2:查询学生姓名、课程名和成绩)
- [例 3:查询选修"数据库系统概论"的学生姓名和成绩](#例 3:查询选修“数据库系统概论”的学生姓名和成绩)
- [例 4:查询李勇选修的课程名和成绩](#例 4:查询李勇选修的课程名和成绩)
- [6. 自然连接](#6. 自然连接)
- [7. 并运算 ∪](#7. 并运算 ∪)
- [8. 差运算 -](#8. 差运算 -)
-
- [例 1:查询没有选课的学生学号](#例 1:查询没有选课的学生学号)
- [例 2:查询没有选修 81001 课程的学生学号](#例 2:查询没有选修 81001 课程的学生学号)
- [例 3:查询选修了 81001 但没有选修 81002 的学生学号](#例 3:查询选修了 81001 但没有选修 81002 的学生学号)
- [9. 除法运算 ÷](#9. 除法运算 ÷)
-
- [例 1:查询选修了全部课程的学生学号](#例 1:查询选修了全部课程的学生学号)
- [例 2:查询至少选修了 20180001 号学生所选全部课程的学生学号](#例 2:查询至少选修了 20180001 号学生所选全部课程的学生学号)
- [三、第三章:SQL 查询重点](#三、第三章:SQL 查询重点)
-
- [1. 基本查询](#1. 基本查询)
-
- [例 1:查询所有学生信息](#例 1:查询所有学生信息)
- [例 2:查询所有课程信息](#例 2:查询所有课程信息)
- [例 3:查询所有选课记录](#例 3:查询所有选课记录)
- [2. DISTINCT 去重查询](#2. DISTINCT 去重查询)
- [3. WHERE 条件查询](#3. WHERE 条件查询)
-
- [例 1:查询计算机科学与技术专业的学生](#例 1:查询计算机科学与技术专业的学生)
- [例 2:查询成绩低于 60 分的选课记录](#例 2:查询成绩低于 60 分的选课记录)
- [例 3:查询 20201 学期的选课记录](#例 3:查询 20201 学期的选课记录)
- [4. BETWEEN 查询](#4. BETWEEN 查询)
-
- [例:查询成绩在 80 到 90 分之间的选课记录](#例:查询成绩在 80 到 90 分之间的选课记录)
- [5. IN 查询](#5. IN 查询)
-
- [例:查询课程号为 81001、81002、81003 的课程信息](#例:查询课程号为 81001、81002、81003 的课程信息)
- [6. LIKE 模糊查询](#6. LIKE 模糊查询)
- [7. NULL 查询](#7. NULL 查询)
- [8. ORDER BY 排序](#8. ORDER BY 排序)
- 四、多表连接查询
-
- [1. 查询每个学生的选课情况](#1. 查询每个学生的选课情况)
- [2. 查询每门课的选课学生姓名](#2. 查询每门课的选课学生姓名)
- [3. 查询 81001 课程的学生姓名和成绩](#3. 查询 81001 课程的学生姓名和成绩)
- [4. 查询"程序设计基础与C语言"课程的学生姓名和成绩](#4. 查询“程序设计基础与C语言”课程的学生姓名和成绩)
- [5. 查询李勇的所有课程名和成绩](#5. 查询李勇的所有课程名和成绩)
- 五、聚集函数与关系代数说明
-
- [1. 查询学生总人数](#1. 查询学生总人数)
- [2. 查询每门课的平均成绩](#2. 查询每门课的平均成绩)
- [3. 查询每个学生的平均成绩](#3. 查询每个学生的平均成绩)
- [4. 查询 81001 课程的最高分](#4. 查询 81001 课程的最高分)
- [5. 查询每门课的选课人数](#5. 查询每门课的选课人数)
- [6. 查询选课人数超过 2 人的课程号](#6. 查询选课人数超过 2 人的课程号)
- 六、嵌套查询
-
- [1. IN 子查询](#1. IN 子查询)
-
- [例:查询选修了 81001 课程的学生姓名](#例:查询选修了 81001 课程的学生姓名)
- [2. NOT EXISTS 子查询](#2. NOT EXISTS 子查询)
-
- [例:查询没有选修 81001 课程的学生姓名](#例:查询没有选修 81001 课程的学生姓名)
- [3. EXISTS 子查询](#3. EXISTS 子查询)
- [4. 查询没有选任何课程的学生姓名](#4. 查询没有选任何课程的学生姓名)
- 七、"全部"类题型
-
- [1. 查询选修了全部课程的学生姓名](#1. 查询选修了全部课程的学生姓名)
- [2. 查询至少选修了李勇所选全部课程的学生姓名](#2. 查询至少选修了李勇所选全部课程的学生姓名)
- [3. 查询至少选修了 20180001 号学生所选全部课程的学生姓名](#3. 查询至少选修了 20180001 号学生所选全部课程的学生姓名)
- 八、常见期末大题模板
-
- [1. 查询"选修了某门课"的学生](#1. 查询“选修了某门课”的学生)
- [2. 查询"没有选修某门课"的学生](#2. 查询“没有选修某门课”的学生)
- [3. 查询"某学生选修的课程"](#3. 查询“某学生选修的课程”)
- [4. 查询"选修了全部课程"的学生](#4. 查询“选修了全部课程”的学生)
- 九、易错点总结
-
- [1. 关系代数投影默认去重,SQL 默认不去重](#1. 关系代数投影默认去重,SQL 默认不去重)
- [2. WHERE 和 HAVING 不要混用](#2. WHERE 和 HAVING 不要混用)
- [3. NULL 不能用等号判断](#3. NULL 不能用等号判断)
- [4. 自然连接不要乱用](#4. 自然连接不要乱用)
- 十、考前速记
课程表(Course)

选课表(SC)

学生表(Student)

数据库系统概论第6版:第二章关系代数与第三章SQL期末重点整理
本文按照以下三张表整理:
Course(Cno, Cname, Credit, Cpno)
SC(Sno, Cno, Grade, Semester, Teachingclass)
Student(Sno, Sname, Sex, Birthday, Smajor)说明:本文每个关系代数表达式都配对应 SQL,每个 SQL 也配对应关系代数表达式。
关系代数主要对应 SQL 查询语句,
CREATE / INSERT / UPDATE / DELETE没有直接的关系代数等价式,本文重点整理期末最常考的查询部分。
一、三张表结构
1. 课程表 Course
text
Course(Cno, Cname, Credit, Cpno)
| 字段 | 含义 |
|---|---|
| Cno | 课程号 |
| Cname | 课程名 |
| Credit | 学分 |
| Cpno | 先修课号 |
2. 选课表 SC
text
SC(Sno, Cno, Grade, Semester, Teachingclass)
| 字段 | 含义 |
|---|---|
| Sno | 学号 |
| Cno | 课程号 |
| Grade | 成绩 |
| Semester | 学期 |
| Teachingclass | 教学班 |
3. 学生表 Student
text
Student(Sno, Sname, Sex, Birthday, Smajor)
| 字段 | 含义 |
|---|---|
| Sno | 学号 |
| Sname | 姓名 |
| Sex | 性别 |
| Birthday | 出生日期 |
| Smajor | 专业 |
二、第二章:关系代数重点
关系代数常考运算:
text
选择 σ
投影 π
并 ∪
差 -
笛卡尔积 ×
连接 ⋈
自然连接 ⋈
除法 ÷
关系代数与 SQL 的大致对应关系:
| 关系代数 | SQL |
|---|---|
| σ | WHERE |
| π | SELECT |
| × | FROM 多表 |
| ⋈ | JOIN ON |
| ∪ | UNION |
| - | NOT EXISTS / NOT IN |
| ÷ | 双重 NOT EXISTS |
1. 选择运算 σ:选行
选择运算用于从表中选出满足条件的元组。
例 1:查询信息安全专业的学生
关系代数:
text
σ Smajor='信息安全'(Student)
SQL:
sql
SELECT *
FROM Student
WHERE Smajor = '信息安全';
例 2:查询男生信息
关系代数:
text
σ Sex='男'(Student)
SQL:
sql
SELECT *
FROM Student
WHERE Sex = '男';
例 3:查询成绩大于等于 90 分的选课记录
关系代数:
text
σ Grade>=90(SC)
SQL:
sql
SELECT *
FROM SC
WHERE Grade >= 90;
2. 投影运算 π:选列
投影运算用于选出指定属性列。
例 1:查询所有学生的学号和姓名
关系代数:
text
π Sno,Sname(Student)
SQL:
sql
SELECT DISTINCT Sno, Sname
FROM Student;
注意:
text
关系代数的投影默认去重,SQL 默认不去重。
如果要和关系代数严格一致,SQL 应使用 DISTINCT。
例 2:查询所有课程的课程号、课程名和学分
关系代数:
text
π Cno,Cname,Credit(Course)
SQL:
sql
SELECT DISTINCT Cno, Cname, Credit
FROM Course;
例 3:查询所有开课学期
关系代数:
text
π Semester(SC)
SQL:
sql
SELECT DISTINCT Semester
FROM SC;
3. 选择 + 投影组合
期末最常见的关系代数题通常是选择和投影组合。
例 1:查询信息安全专业学生的姓名
关系代数:
text
π Sname(σ Smajor='信息安全'(Student))
SQL:
sql
SELECT DISTINCT Sname
FROM Student
WHERE Smajor = '信息安全';
例 2:查询 81001 课程的选课学生学号和成绩
关系代数:
text
π Sno,Grade(σ Cno='81001'(SC))
SQL:
sql
SELECT DISTINCT Sno, Grade
FROM SC
WHERE Cno = '81001';
例 3:查询学分为 4 的课程名
关系代数:
text
π Cname(σ Credit=4(Course))
SQL:
sql
SELECT DISTINCT Cname
FROM Course
WHERE Credit = 4;
4. 笛卡尔积 ×
笛卡尔积会把两个关系中的元组两两组合。
单独考得不多,通常配合选择运算构造连接。
例:查询学生姓名和其选课成绩
关系代数:
text
π Sname,Cno,Grade(σ Student.Sno=SC.Sno(Student × SC))
SQL:
sql
SELECT DISTINCT Student.Sname, SC.Cno, SC.Grade
FROM Student, SC
WHERE Student.Sno = SC.Sno;
5. 连接运算 ⋈
连接运算是多表查询的核心。
例 1:查询学生姓名、课程号和成绩
关系代数:
text
π Sname,Cno,Grade(Student ⋈ Student.Sno=SC.Sno SC)
SQL:
sql
SELECT DISTINCT S.Sname, SC.Cno, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno;
例 2:查询学生姓名、课程名和成绩
关系代数:
text
π Sname,Cname,Grade((Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course)
SQL:
sql
SELECT DISTINCT S.Sname, C.Cname, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno;
例 3:查询选修"数据库系统概论"的学生姓名和成绩
关系代数:
text
π Sname,Grade(σ Cname='数据库系统概论'((Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course))
SQL:
sql
SELECT DISTINCT S.Sname, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno
WHERE C.Cname = '数据库系统概论';
例 4:查询李勇选修的课程名和成绩
关系代数:
text
π Cname,Grade(σ Sname='李勇'((Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course))
SQL:
sql
SELECT DISTINCT C.Cname, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno
WHERE S.Sname = '李勇';
6. 自然连接
自然连接会自动按照两个关系中同名属性进行连接。
Student 和 SC 有同名属性 Sno,可以自然连接。
SC 和 Course 有同名属性 Cno,也可以自然连接。
例:查询学生姓名、课程名和成绩
关系代数:
text
π Sname,Cname,Grade(Student ⋈ SC ⋈ Course)
SQL:
sql
SELECT DISTINCT Sname, Cname, Grade
FROM Student
NATURAL JOIN SC
NATURAL JOIN Course;
考试提醒:
text
自然连接会自动按所有同名字段连接。
如果不确定同名字段是否唯一,建议用 JOIN ON 写清楚连接条件。
等价的更稳妥写法:
关系代数:
text
π Sname,Cname,Grade((Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course)
SQL:
sql
SELECT DISTINCT S.Sname, C.Cname, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno;
7. 并运算 ∪
并运算用于合并两个查询结果。
例:查询信息安全专业或数据科学与大数据技术专业的学生学号
关系代数:
text
π Sno(σ Smajor='信息安全'(Student)) ∪ π Sno(σ Smajor='数据科学与大数据技术'(Student))
SQL:
sql
SELECT Sno
FROM Student
WHERE Smajor = '信息安全'
UNION
SELECT Sno
FROM Student
WHERE Smajor = '数据科学与大数据技术';
也可以写成:
关系代数:
text
π Sno(σ Smajor='信息安全' OR Smajor='数据科学与大数据技术'(Student))
SQL:
sql
SELECT DISTINCT Sno
FROM Student
WHERE Smajor = '信息安全'
OR Smajor = '数据科学与大数据技术';
8. 差运算 -
差运算常用于表达"没有""未选""不属于"。
例 1:查询没有选课的学生学号
关系代数:
text
π Sno(Student) - π Sno(SC)
SQL:
sql
SELECT S.Sno
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
);
例 2:查询没有选修 81001 课程的学生学号
关系代数:
text
π Sno(Student) - π Sno(σ Cno='81001'(SC))
SQL:
sql
SELECT S.Sno
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
AND SC.Cno = '81001'
);
例 3:查询选修了 81001 但没有选修 81002 的学生学号
关系代数:
text
π Sno(σ Cno='81001'(SC)) - π Sno(σ Cno='81002'(SC))
SQL:
sql
SELECT SC1.Sno
FROM SC SC1
WHERE SC1.Cno = '81001'
AND NOT EXISTS (
SELECT *
FROM SC SC2
WHERE SC2.Sno = SC1.Sno
AND SC2.Cno = '81002'
);
9. 除法运算 ÷
除法是期末难点,常用于表达:
text
全部
所有
至少包含全部
例 1:查询选修了全部课程的学生学号
关系代数:
text
π Sno,Cno(SC) ÷ π Cno(Course)
SQL:
sql
SELECT S.Sno
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM Course C
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
AND SC.Cno = C.Cno
)
);
理解:
text
不存在一门课程,该学生没有选修。
例 2:查询至少选修了 20180001 号学生所选全部课程的学生学号
关系代数:
text
π Sno,Cno(SC) ÷ π Cno(σ Sno='20180001'(SC))
SQL:
sql
SELECT S.Sno
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM SC SC1
WHERE SC1.Sno = '20180001'
AND NOT EXISTS (
SELECT *
FROM SC SC2
WHERE SC2.Sno = S.Sno
AND SC2.Cno = SC1.Cno
)
);
理解:
text
不存在一门 20180001 选过的课,而该学生没有选。
三、第三章:SQL 查询重点
第三章 SQL 最核心的内容是查询。下面所有 SQL 都给出对应关系代数。
1. 基本查询
例 1:查询所有学生信息
SQL:
sql
SELECT *
FROM Student;
关系代数:
text
Student
例 2:查询所有课程信息
SQL:
sql
SELECT *
FROM Course;
关系代数:
text
Course
例 3:查询所有选课记录
SQL:
sql
SELECT *
FROM SC;
关系代数:
text
SC
2. DISTINCT 去重查询
例:查询所有专业名称
SQL:
sql
SELECT DISTINCT Smajor
FROM Student;
关系代数:
text
π Smajor(Student)
3. WHERE 条件查询
例 1:查询计算机科学与技术专业的学生
SQL:
sql
SELECT *
FROM Student
WHERE Smajor = '计算机科学与技术';
关系代数:
text
σ Smajor='计算机科学与技术'(Student)
例 2:查询成绩低于 60 分的选课记录
SQL:
sql
SELECT *
FROM SC
WHERE Grade < 60;
关系代数:
text
σ Grade<60(SC)
例 3:查询 20201 学期的选课记录
SQL:
sql
SELECT *
FROM SC
WHERE Semester = '20201';
关系代数:
text
σ Semester='20201'(SC)
4. BETWEEN 查询
例:查询成绩在 80 到 90 分之间的选课记录
SQL:
sql
SELECT *
FROM SC
WHERE Grade BETWEEN 80 AND 90;
关系代数:
text
σ Grade>=80 AND Grade<=90(SC)
5. IN 查询
例:查询课程号为 81001、81002、81003 的课程信息
SQL:
sql
SELECT *
FROM Course
WHERE Cno IN ('81001', '81002', '81003');
关系代数:
text
σ Cno='81001' OR Cno='81002' OR Cno='81003'(Course)
6. LIKE 模糊查询
例:查询课程名中包含"数据库"的课程
SQL:
sql
SELECT *
FROM Course
WHERE Cname LIKE '%数据库%';
关系代数:
text
σ Cname LIKE '%数据库%'(Course)
说明:
text
严格的传统关系代数中没有 LIKE,这是 SQL 的字符串匹配条件。
考试中可把 LIKE 当作选择条件写在 σ 中。
7. NULL 查询
例:查询没有先修课的课程
SQL:
sql
SELECT *
FROM Course
WHERE Cpno IS NULL;
关系代数:
text
σ Cpno IS NULL(Course)
注意:
text
NULL 不能写成 = NULL,必须写 IS NULL。
8. ORDER BY 排序
关系代数本身是集合运算,不强调顺序,因此传统关系代数没有排序运算。
考试中如果要求 SQL 和关系代数互译,排序通常只在 SQL 中体现。
例:查询选课记录,按成绩降序排列
SQL:
sql
SELECT *
FROM SC
ORDER BY Grade DESC;
关系代数:
text
SC
补充说明:
text
ORDER BY 是 SQL 的显示排序要求,传统关系代数不表达结果顺序。
四、多表连接查询
1. 查询每个学生的选课情况
SQL:
sql
SELECT S.Sno, S.Sname, SC.Cno, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno;
关系代数:
text
π Student.Sno,Sname,Cno,Grade(Student ⋈ Student.Sno=SC.Sno SC)
2. 查询每门课的选课学生姓名
SQL:
sql
SELECT C.Cname, S.Sname
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno;
关系代数:
text
π Cname,Sname((Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course)
3. 查询 81001 课程的学生姓名和成绩
SQL:
sql
SELECT S.Sname, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno
WHERE SC.Cno = '81001';
关系代数:
text
π Sname,Grade(σ Cno='81001'(Student ⋈ Student.Sno=SC.Sno SC))
4. 查询"程序设计基础与C语言"课程的学生姓名和成绩
SQL:
sql
SELECT S.Sname, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno
WHERE C.Cname = '程序设计基础与C语言';
关系代数:
text
π Sname,Grade(σ Cname='程序设计基础与C语言'((Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course))
5. 查询李勇的所有课程名和成绩
SQL:
sql
SELECT C.Cname, SC.Grade
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno
WHERE S.Sname = '李勇';
关系代数:
text
π Cname,Grade(σ Sname='李勇'((Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course))
五、聚集函数与关系代数说明
SQL 中的聚集函数包括:
text
COUNT
SUM
AVG
MAX
MIN
传统关系代数不直接包含聚集函数。
不过数据库课程中有时会使用扩展关系代数,用 γ 表示分组和聚集。
下面用扩展关系代数表示。
1. 查询学生总人数
SQL:
sql
SELECT COUNT(*) AS StudentCount
FROM Student;
扩展关系代数:
text
γ COUNT(*)→StudentCount(Student)
2. 查询每门课的平均成绩
SQL:
sql
SELECT Cno, AVG(Grade) AS AvgGrade
FROM SC
GROUP BY Cno;
扩展关系代数:
text
γ Cno; AVG(Grade)→AvgGrade(SC)
3. 查询每个学生的平均成绩
SQL:
sql
SELECT Sno, AVG(Grade) AS AvgGrade
FROM SC
GROUP BY Sno;
扩展关系代数:
text
γ Sno; AVG(Grade)→AvgGrade(SC)
4. 查询 81001 课程的最高分
SQL:
sql
SELECT MAX(Grade) AS MaxGrade
FROM SC
WHERE Cno = '81001';
扩展关系代数:
text
γ MAX(Grade)→MaxGrade(σ Cno='81001'(SC))
5. 查询每门课的选课人数
SQL:
sql
SELECT Cno, COUNT(*) AS Num
FROM SC
GROUP BY Cno;
扩展关系代数:
text
γ Cno; COUNT(*)→Num(SC)
6. 查询选课人数超过 2 人的课程号
SQL:
sql
SELECT Cno
FROM SC
GROUP BY Cno
HAVING COUNT(*) > 2;
扩展关系代数:
text
π Cno(σ Num>2(γ Cno; COUNT(*)→Num(SC)))
六、嵌套查询
1. IN 子查询
例:查询选修了 81001 课程的学生姓名
SQL:
sql
SELECT Sname
FROM Student
WHERE Sno IN (
SELECT Sno
FROM SC
WHERE Cno = '81001'
);
关系代数:
text
π Sname(Student ⋈ Student.Sno=SC.Sno σ Cno='81001'(SC))
也可以写成:
text
π Sname(σ Cno='81001'(Student ⋈ Student.Sno=SC.Sno SC))
2. NOT EXISTS 子查询
例:查询没有选修 81001 课程的学生姓名
SQL:
sql
SELECT Sname
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
AND SC.Cno = '81001'
);
关系代数:
text
π Sname(Student ⋈ (π Sno(Student) - π Sno(σ Cno='81001'(SC))))
更清晰地写成两步:
text
T = π Sno(Student) - π Sno(σ Cno='81001'(SC))
π Sname(Student ⋈ T)
3. EXISTS 子查询
例:查询至少选修了一门课程的学生姓名
SQL:
sql
SELECT Sname
FROM Student S
WHERE EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
);
关系代数:
text
π Sname(Student ⋈ Student.Sno=SC.Sno SC)
4. 查询没有选任何课程的学生姓名
SQL:
sql
SELECT Sname
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
);
关系代数:
text
T = π Sno(Student) - π Sno(SC)
π Sname(Student ⋈ T)
七、"全部"类题型
看到:
text
所有
全部
每一门
至少包含全部
优先想到:
text
关系代数:除法 ÷
SQL:双重 NOT EXISTS
1. 查询选修了全部课程的学生姓名
SQL:
sql
SELECT Sname
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM Course C
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
AND SC.Cno = C.Cno
)
);
关系代数:
text
T = π Sno,Cno(SC) ÷ π Cno(Course)
π Sname(Student ⋈ T)
2. 查询至少选修了李勇所选全部课程的学生姓名
SQL:
sql
SELECT S2.Sname
FROM Student S2
WHERE NOT EXISTS (
SELECT *
FROM SC SC1
JOIN Student S1 ON SC1.Sno = S1.Sno
WHERE S1.Sname = '李勇'
AND NOT EXISTS (
SELECT *
FROM SC SC2
WHERE SC2.Sno = S2.Sno
AND SC2.Cno = SC1.Cno
)
);
关系代数:
text
T1 = π Cno(σ Sname='李勇'(Student ⋈ Student.Sno=SC.Sno SC))
T2 = π Sno,Cno(SC) ÷ T1
π Sname(Student ⋈ T2)
3. 查询至少选修了 20180001 号学生所选全部课程的学生姓名
SQL:
sql
SELECT S.Sname
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM SC SC1
WHERE SC1.Sno = '20180001'
AND NOT EXISTS (
SELECT *
FROM SC SC2
WHERE SC2.Sno = S.Sno
AND SC2.Cno = SC1.Cno
)
);
关系代数:
text
T = π Sno,Cno(SC) ÷ π Cno(σ Sno='20180001'(SC))
π Sname(Student ⋈ T)
八、常见期末大题模板
1. 查询"选修了某门课"的学生
SQL 模板:
sql
SELECT S.Sname
FROM Student S
JOIN SC ON S.Sno = SC.Sno
WHERE SC.Cno = '课程号';
关系代数模板:
text
π Sname(σ Cno='课程号'(Student ⋈ Student.Sno=SC.Sno SC))
2. 查询"没有选修某门课"的学生
SQL 模板:
sql
SELECT S.Sname
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
AND SC.Cno = '课程号'
);
关系代数模板:
text
T = π Sno(Student) - π Sno(σ Cno='课程号'(SC))
π Sname(Student ⋈ T)
3. 查询"某学生选修的课程"
SQL 模板:
sql
SELECT C.Cname
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno
WHERE S.Sname = '学生姓名';
关系代数模板:
text
π Cname(σ Sname='学生姓名'((Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course))
4. 查询"选修了全部课程"的学生
SQL 模板:
sql
SELECT S.Sname
FROM Student S
WHERE NOT EXISTS (
SELECT *
FROM Course C
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE SC.Sno = S.Sno
AND SC.Cno = C.Cno
)
);
关系代数模板:
text
T = π Sno,Cno(SC) ÷ π Cno(Course)
π Sname(Student ⋈ T)
九、易错点总结
1. 关系代数投影默认去重,SQL 默认不去重
关系代数:
text
π Smajor(Student)
SQL 应写:
sql
SELECT DISTINCT Smajor
FROM Student;
2. WHERE 和 HAVING 不要混用
SQL:
sql
SELECT Cno, COUNT(*) AS Num
FROM SC
GROUP BY Cno
HAVING COUNT(*) > 2;
扩展关系代数:
text
σ Num>2(γ Cno; COUNT(*)→Num(SC))
3. NULL 不能用等号判断
SQL:
sql
SELECT *
FROM Course
WHERE Cpno IS NULL;
关系代数:
text
σ Cpno IS NULL(Course)
错误写法:
sql
WHERE Cpno = NULL
4. 自然连接不要乱用
自然连接:
text
Student ⋈ SC ⋈ Course
对应 SQL:
sql
SELECT *
FROM Student
NATURAL JOIN SC
NATURAL JOIN Course;
更推荐写清楚连接条件:
关系代数:
text
(Student ⋈ Student.Sno=SC.Sno SC) ⋈ SC.Cno=Course.Cno Course
SQL:
sql
SELECT *
FROM Student S
JOIN SC ON S.Sno = SC.Sno
JOIN Course C ON SC.Cno = C.Cno;
十、考前速记
text
σ 是选择,对应 WHERE,负责选行。
π 是投影,对应 SELECT,负责选列。
× 是笛卡尔积,多表组合。
⋈ 是连接,对应 JOIN ON。
∪ 是并,对应 UNION。
- 是差,常用 NOT EXISTS 表示"没有"。
÷ 是除法,常用双重 NOT EXISTS 表示"全部"。
看到"没有",想到差运算和 NOT EXISTS。
看到"全部",想到除法和双重 NOT EXISTS。
看到"每门课平均成绩",想到 GROUP BY 和 AVG。
看到"选课人数超过多少",想到 GROUP BY 和 HAVING。