关系模型与关系代数
关系模型(Relational Model)把数据抽象为"关系"(表),用集合论的运算(选择、投影、连接)来操作数据——SQL 就是基于关系代数设计的
🧮 ER 图画完了——然后呢?
上一节我们用 ER 图画出了选课系统的”设计图”:学生、课程、教师……看起来很清楚。
但问题是——计算机看不懂 ER 图。你不可能把一张图直接交给 MySQL 说”照着这个建表”。
我们需要一种方式把 ER 图转化为计算机能理解的形式。1970 年,IBM 的研究员 Edgar Codd 提出了一个天才的方案——关系模型(Relational Model)。
这个模型极为简单优雅:所有数据都放在”表”里,用集合论的运算来操作数据。 今天几乎所有主流数据库(MySQL、PostgreSQL、Oracle、SQL Server)都基于这个模型。
🏫 类比:用集合论做菜
假设你有一个冰箱(数据库),里面有各种食材(数据)。
没有关系模型 → 你要自己翻冰箱,一个个看,把需要的找出来。
有关系模型 → 你只需要说”给我拿冷藏室里所有的鸡蛋”(选择)、“只要鸡蛋的生产日期列”(投影)、“把鸡蛋和西红柿搭配在一起”(连接)。
关系代数的本质就是:用集合运算来操作数据。
📊 关系(Relation)就是”表”
关系模型的核心概念极简单:
关系(Relation) = 一张有行有列的表(Table)
学生关系(Students):
┌──────┬────────┬────┬───────────┐
│ 学号 │ 姓名 │ 年龄 │ 班级 │
├──────┼────────┼────┼───────────┤
│ 001 │ 张三 │ 20 │ 计科1班 │
│ 002 │ 李四 │ 21 │ 计科2班 │
│ 003 │ 王五 │ 19 │ 计科1班 │
└──────┴────────┴────┴───────────┘
每个”关系”有一个名字(比如 Students),每一列有列名(学号、姓名、年龄、班级),每一行是一条具体记录。
关系模型的三个要点:
- 每一列是原子的(Atomic)——每个单元格只能存一个值,不能是一个列表。比如”班级”列只能写”计科1班”,不能写”计科1班、计科2班”
- 没有重复行——关系是个集合,集合里没有重复元素
- 行的顺序不重要——换个顺序,还是同一个关系
🔢 关系代数——操纵数据的”数学语言”
关系代数(Relational Algebra)是用集合运算来操作关系的一套符号系统。它定义了8 种基本运算,但常用的只有 3 种。
为什么需要关系代数?
因为 SQL 是给”人”看的——它用英语单词(SELECT、FROM、WHERE),理解起来直观。但计算机需要更底层的、没有歧义的数学表示。关系代数就是这个”中间层”:
你的 SQL 查询
↓
关系代数表达式(优化器在这里做优化)
↓
执行计划(计算机真正执行)
① 选择(Selection)——选行
符号:σ(sigma,希腊字母 σ)
选择操作从关系中选出满足条件的行。
σ年龄 > 20(Students)
结果:
┌──────┬────────┬────┬───────────┐
│ 002 │ 李四 │ 21 │ 计科2班 │
└──────┴────────┴────┴───────────┘
SQL 对应:SELECT * FROM Students WHERE age > 20
② 投影(Projection)——选列
符号:π(pi,希腊字母 π)
投影操作从关系中选出指定的列。
π姓名, 班级(Students)
结果:
┌────────┬───────────┐
│ 姓名 │ 班级 │
├────────┼───────────┤
│ 张三 │ 计科1班 │
│ 李四 │ 计科2班 │
│ 王五 │ 计科1班 │
└────────┴───────────┘
SQL 对应:SELECT 姓名, 班级 FROM Students
③ 连接(Join)——合并表
符号:⋈(bowtie,领结符)
连接操作把两个关系按条件合并成一个新关系。
Students ⋈学号=StudentID Enrollments
把学生表和选课表按"学号匹配"合并:
学号│姓名│年龄│班级│课程号│成绩
────────────────────────────
001 │张三│ 20 │计科1班│ CS101 │ 85
001 │张三│ 20 │计科1班│ CS102 │ 92
002 │李四│ 21 │计科2班│ CS101 │ 78
SQL 对应:SELECT * FROM Students JOIN Enrollments ON Students.学号 = Enrollments.StudentID
连接是关系代数中最强大的操作——它让你能把分散在不同表中的信息”重新组合”起来。
🧩 用关系代数表达完整查询
假设我们想查**“所有选了’数据结构’这门课的学生姓名”**:
π姓名(
σ课程名='数据结构'(
Students ⋈ Enrollments ⋈ Courses
)
)
执行过程:
- 连接:把学生表、选课记录表、课程表按关联条件合并
- 选择:只保留课程名为”数据结构”的行
- 投影:只要姓名这一列
这就是 SQL 在底层做的事——你的每个 SQL 查询,数据库都会先转换成关系代数表达式,再做优化和执行。
📋 其他关系代数运算
| 运算 | 符号 | 含义 | SQL 对应 |
|---|---|---|---|
| 并 | ∪ | 合并两个关系的所有行 | UNION |
| 差 | − | 在关系 A 但不在关系 B 的行 | EXCEPT |
| 交 | ∩ | 同时在 A 和 B 中的行 | INTERSECT |
| 笛卡尔积 | × | A 的每行和 B 的每行组合 | CROSS JOIN |
| 重命名 | ρ | 给关系或列改名 | AS |
并和差的一个实际例子
两个班各自有学生名单:
计科1班(学号│姓名) 计科2班(学号│姓名)
001│张三 004│赵六
002│李四 005│钱七
003│王五 006│孙八
计科1班 ∪ 计科2班 → 全部6个学生(并集)
计科1班 − 计科2班 → 张三、李四、王五(只在1班的学生)
🗺️ 从 ER 图到关系模型的转换
现在你可以理解如何把 ER 图转换成关系模型了——这就是”数据库设计”从概念到实现的关键一步:
| ER 模型 | 关系模型 |
|---|---|
| 实体 → | 关系(表) |
| 属性 → | 列 |
| 主键 → | 主键列 |
| 关系 1:N → | 在 N 端加外键 |
| 关系 M:N → | 新建一张中间表,加两个外键 |
例子: 上一节的选课系统 ER 图转成关系模型:
Students(学号, 姓名, 年龄, 班级)
Courses(课程号, 课程名, 学分)
Enrollments(学号, 课程号, 成绩) ← 中间表,有两个外键
Teachers(工号, 姓名, 职称)
多对多关系(M:N)为什么需要中间表? 因为一张表的”行”没法直接对应多行另一张表的”多行”——中间表的每一行恰好表示”一个学生选了一门课”这一个关系,完美化解 M:N。
📝 小结
| 概念 | 一句话 |
|---|---|
| 关系(Relation) | 一张有行有列的表,是关系模型的基本单位 |
| 选择(σ) | 选出满足条件的行(WHERE) |
| 投影(π) | 选出指定的列(SELECT 列) |
| 连接(⋈) | 按条件合并两个关系(JOIN) |
| 关系代数 | SQL 的数学基础,数据库优化器用它表示查询 |
💡 重要理解:关系代数看起来像是”数学课上的东西”,但它是每个 SQL 查询在数据库内部被执行的真实方式。理解了关系代数,你就真正理解了 SQL”为什么这么设计”。
为什么先学这个? 关系代数是 SQL 的理论基础。掌握了关系模型的思维方式,下一步就可以开始真正写 SQL——SQL 基础(DDL, DML)。