关系代数是闭卷考核内容之一,对应教材第2章的“关系代数”部分。教材指出:关系代数是一种抽象的查询语言,它以关系作为运算对象,以关系作为运算结果,通过运算表达查询需求。
这部分最常见的题型不是背概念,而是:
给出若干关系模式和一个查询要求,要求写出关系代数表达式。
所以复习重点是:认识运算符、确定操作顺序、能把中文查询翻译成表达式。
可以把关系理解为一张二维表:
| 数据库术语 | 表格中的直观含义 | 示例 |
|---|
| 关系 | 一张表 | Student |
| 元组 | 一行记录 | 某个学生的一行信息 |
| 属性 | 一列 | Sno、Sname |
| 域 | 列的取值范围 | 学号字符串、成绩数值 |
| 候选码 | 能唯一标识元组的最小属性组 | Sno |
后面的例题统一使用教材中常见的学生选课关系:
Student(Sno, Sname, Ssex, Smajor)Course(Cno, Cname, Credit)SC(Sno, Cno, Grade)
其中,Student 表示学生,Course 表示课程,SC 表示选课联系:一个学生可以选多门课,一门课可以被多名学生选择。
| 运算 | 表达式 | 用途 | 直观理解 |
|---|
| 并 | R∪S | 合并属于 R 或 S 的元组 | 两张同结构表合并去重 |
| 差 | R−S | 取属于 R 但不属于 S 的元组 | 在 R 中排除 S |
| 交 | R∩S | 取同时属于 R 和 S 的元组 | 两表共同部分 |
| 笛卡尔积 | R×S | 任意组合两关系中的元组 | 每行和另一表每行配对 |
| 选择 | σF(R) | 按条件 F 筛选行 | SQL 中的 WHERE |
| 投影 | πA1,A2,…,An(R) | 选取指定属性并去重 | SQL 中的列选择 |
| 条件连接 | R⋈FS | 按条件 F 组合元组 | SQL 中的 JOIN ... ON |
| 自然连接 | R⋈S | 按同名属性相等连接并去重列 | 常用于有关联属性的表 |
| 除 | R÷S | 表达满足 S 中全部取值 | 查询选修了全部课程的学生 |
必须特别注意:
并、差、交要求参与运算的关系具有相同或相容的属性结构。选择作用于行,投影作用于列。连接不是简单地任意组合两表数据,而是在组合结果上保留符合关联条件的元组。
选择是从关系中挑出满足条件的元组,也就是筛选行。形式为:
σF(R)
其中,R 为被操作的关系,F 为选择条件。
查询计算机科学与技术专业的学生:
σSmajor=’计算机科学与技术’(Student)
查询成绩不低于 90 分的选课记录:
σGrade≥90(SC)
查询计算机科学与技术专业的女生:
σSmajor=’计算机科学与技术’∧Ssex=’女’(Student)
下面写法是错误的,因为它把筛选条件写在了投影运算中:
πSmajor=’计算机科学与技术’(Student)
应记住:σ 后写筛选条件,π 后写要保留的属性列。
投影从关系中选择需要显示的属性列。形式为:
πA1,A2,…,An(R)
查询所有学生的学号和姓名:
πSno,Sname(Student)
查询所有被选择过的课程号:
πCno(SC)
因为关系不允许重复元组,如果很多学生选了同一门课,投影后的课程号结果中相同课程只保留一次。
查询计算机科学与技术专业学生的学号和姓名:
πSno,Sname(σSmajor=’计算机科学与技术’(Student))
运算顺序为:先通过 σ 筛出符合专业条件的学生,再通过 π 仅显示学号与姓名。
集合运算的两个关系必须相容,通常要求属性个数相同,并且对应属性来自相同或可比较的域。
例如,可以把两个只包含属性 Sno 的查询结果进行集合运算,但不能直接将 Student 与 Course 做并运算。
查询选修课程 C01 或 C02 的学生学号:
πSno(σCno=’C01’(SC)) ∪ πSno(σCno=’C02’(SC))
查询同时选修课程 C01 和 C02 的学生学号:
πSno(σCno=’C01’(SC)) ∩ πSno(σCno=’C02’(SC))
查询选修了 C01 但没有选修 C02 的学生学号:
πSno(σCno=’C01’(SC))−πSno(σCno=’C02’(SC))
如果关系 Student 有 5 行,关系 SC 有 20 行,则:
Student×SC
产生的元组数量为:
5×20=100
其中很多组合并没有实际业务意义。因此实际查询通常要在笛卡尔积基础上增加连接条件。
查询学生姓名及其选课成绩,需要将学生和选课记录按照相同学号连接:
πStudent.Sno,Sname,Cno,Grade(Student⋈Student.Sno=SC.SnoSC)
这相当于 SQL 中的:
SELECT Student.Sno, Sname, Cno, Grade
JOIN SC ON Student.Sno = SC.Sno;
等值连接是连接条件使用等号的连接。例如:
Student⋈Student.Sno=SC.SnoSC
自然连接会在两个关系的同名且可比较属性上进行相等连接,并去掉结果中重复出现的同名连接属性。
若 Student 和 SC 的同名联系属性只有 Sno,则可以简写为:
Student⋈SC
考试中如果同名属性含义不够明确,写出连接条件通常更稳妥。
题目:查询“选修了课程名称为数据库系统的学生学号和姓名”。
需要使用三个关系:Student 提供姓名,SC 表示学生选了哪门课程,Course 提供课程名称。
若使用自然连接表达,可以写为:
πSno,Sname(Student⋈SC⋈σCname=’数据库系统’(Course))
如果需要显式写连接条件,则可写为:
πStudent.Sno,Sname((Student⋈Student.Sno=SC.SnoSC)⋈SC.Cno=Course.CnoσCname=’数据库系统’(Course))
解题顺序是:先找出课程名称对应的课程,再通过 SC 找到选修者,最后通过 Student 显示学生姓名,并用投影只保留题目要求的列。
题目中出现“全部”“所有”“每一门”“至少包含指定集合中的每一个”等表述时,应首先想到除运算。
一般形式是:
R(X,Y)÷S(Y)
其结果保留满足下列条件的 X:
∀y∈S,(x,y)∈R
也就是:对 S 中的每一个对象,x 都与之存在匹配关系。
在关系 SC(Sno,Cno,Grade) 中,先保留学生号与课程号:
πSno,Cno(SC)
全部课程号集合为:
πCno(Course)
因此表达式为:
πSno,Cno(SC)÷πCno(Course)
结果为满足“对全部课程号都存在选课记录”的学生学号。
指定学生 S01 所选课程集合为:
πCno(σSno=’S01’(SC))
所有学生选课关系除以该课程集合:
πSno,Cno(SC)÷πCno(σSno=’S01’(SC))
仍使用以下三个关系:
Student(Sno, Sname, Ssex, Smajor)Course(Cno, Cname, Credit)SC(Sno, Cno, Grade)
πSno,Sname(σSsex=’女’(Student))
πSno(σCno=’C01’∧Grade>80(SC))
πSname(Student⋈σCno=’C01’(SC))
πSno(Student)−πSno(σCno=’C01’(SC))
这里不能只从 SC 中找课程号“不等于 C01”的记录,因为一个学生可能同时选了 C01 和其他课程。
πSno(σCno=’C01’(SC))∩πSno(σCno=’C02’(SC))
πSname,Cname(Student⋈SC⋈σCredit≥3(Course))
先得到满足条件的学号,再与学生关系连接:
πSname(Student⋈(πSno,Cno(SC)÷πCno(Course)))
| 关系代数 | SQL中的对应思路 |
|---|
| σF(R) | SELECT ... FROM R WHERE F |
| πA,B(R) | SELECT DISTINCT A, B FROM R |
| R⋈FS | R JOIN S ON F |
| R∪S | UNION |
| R−S | EXCEPT 或差集逻辑 |
| R∩S | INTERSECT |
| R÷S | 通常用双重 NOT EXISTS 或分组计数表达 |
对照 SQL 有助于理解,但闭卷题目要求写关系代数时,必须写正确符号和运算顺序。
题目只要求姓名,却把整个连接结果写出来。正确表达式最外层通常要有投影:
πSname(⋯)
下面这种写法是错误的:
πSno(σCno=’C01’(SC))
该结果会错误包含“既选了 C01 又选了其他课程”的学生。正确方法使用差集:
πSno(Student)−πSno(σCno=’C01’(SC))
σ: 筛行,下面标注筛选条件π: 选列,下面标注保留属性
题干中只要出现“所有课程”“每个项目”“全部指定对象”,就优先判断是否需要使用除运算 ÷。
关系代数以关系为运算对象,以关系为运算结果。传统集合运算包括并、差、交和笛卡尔积;专门的关系运算包括选择、投影、连接和除。
选择运算 σ 用于筛选满足条件的元组,即筛行;投影运算 π 用于选取需要的属性,即选列;连接运算 ⋈ 用于根据相关属性把不同关系中的数据组合起来;自然连接会去掉重复的同名连接属性;除运算 ÷ 常用于表达“满足全部条件”的查询。
写表达式时应先确定需要哪些关系和联系,再写筛选条件,最后投影题目要求输出的属性。查询“没有”通常用差运算,查询“全部”通常考虑除运算。