跳转至

第 2 章:关系模型

简单又优雅。

一个使用关系模型的数据库储存了一些关系;一个“关系”是包含行和列的表。

注意

关系 relation 指的是一张表,联系 relationship 指的是一些实体之间的关联。

基本结构

对于集合 \(D_1, D_2, \cdots\),它们的关系 \(r\) 是它们笛卡尔积 \(D_1 \times D_2 \times \cdots\) 的一个子集。

属性 Attribute

每个属性都有其(值)域 domain。根据第一范式,属性的值需要是原子化的,也就是说不能是多个值组合起来。

id name gender species tags
1 妙艾拉 魔幻假面喵 挚友、美食家、…… ←这样不行!

所有的域里面都包含 null 这个值。虽然 null 总会让数据处理变得复杂……

关系模式 Relation Schema

关系模式描述了一个关系的结构,比如:

Instructor-schema = (ID: string, name: string, dept_name: string, salary: int)

现在有属性 \(A_1, A_2, \cdots, A_n\),那么 \(R = (A_1, A_2, \cdots, A_n)\) 称为一个关系模式;\(r(R)\) 是在关系模式 R 下的一个关系。

关系实例 Relation Instance

关系实例是某个时候关系里面的数据的快照。

如果现在有元组 t,那么 \(t[A]\) 代表元组 t 中名为 A 的属性的值。

关系的特性

  • 无序
  • 不重

码 Keys

为了让 K(\(K \subset R\))成为关系模式 R 的超码(Superkey),它需要满足只用 K 里面的属性就可以唯一确定所有的元组。比如对于下面这个:

id name gender species
1 妙艾拉 魔幻假面喵
624 蒙洛卡 魔幻假面喵
993 皎华 月亮伊布

\(K = \{\mathrm{id}\}\) 可以,\(K = \{\mathrm{name}\}\) 可以,\(K = \{\mathrm{gender}, \mathrm{species}\}\) 也可以;但是 \(K = \{\mathrm{gender}\}\)\(K = \{\mathrm{species}\}\) 都不行。

而其中,候选码(Candidate Key)是所有超码里面小的那几个,也就是候选码的真子集不能再组成超码。所以 \(K = \{\mathrm{gender}, \mathrm{species}\}\) 也是一个候选码。

主码(Primary Key)是用户选定的一个候选码,表示的时候要打一个下划线。比如我们可以让 \(K = \{\mathrm{id}\}\) 变成主码,因为名字还是太容易重复了:

Teammates(id, name, gender, species),这样。

此时我们拿来宝可梦图鉴 Pokédex(species, ...),那么属性 species 叫做 Teammates 引用 Pokédex 的一个外码。Teammates 是参照关系(referencing relation),Pokédex 是被参照关系(referenced relation)。参照关系中,外码的值必须在被参照关系中实际存在或为 null,所以你不能把什么祖利兽加进 Teammates 里面,因为他不是宝可梦。

六个基本操作

选择 \(\sigma_{p}(r)\)

设选择谓词 p 是一堆条件组成的约束,那么 \(\sigma_{p}(r) = \{t | t \in r \land p(t)\}\) 是 r 中满足 p 的元素。

比如 \(\sigma_{\mathrm{affection} \geq 255}(\mathrm{pokemon})\) 会返回所有友好度大于等于 255 的宝可梦。

投影 \(\Pi_{A_1 \cdots A_k}(r)\)

\(A_1 \cdots A_k\) 是属性的名字,那么 \(\Pi_{A_1 \cdots A_k}(r)\) 是从 r 中只选出属性 \(A_1 \cdots A_k\) 后组成的表。注意因为这是个集合,所以得去重。

\(r \cup s = \{t | t \in r \lor t \in s\}\),其中 r 和 s 需要等目(属性的个数一样),并且相应的属性可以合并起来。

比如 \(\Pi_{\mathrm{name}}(\mathrm{trainer}) \cup \Pi_{\mathrm{name}}(\mathrm{pokemon})\) 会返回所有训练师和宝可梦的名字(在一张表上)。

\(r - s = \{t | t \in r \land t \notin s\}\),和上面一样,其中 r 和 s 需要等目(属性的个数一样),并且相应的属性可以合并起来。

广义笛卡尔积

\(r \times s = \{\{t, q\} | t \in r \land q \in s\}\),其中如果 r 和 s 里面有属性的名字相同,那么必须给个新名字以区分。

重命名 \(\rho_{x}(E); ~\rho_{x(A_1, \cdots, A_n)}(E)\)

现在有个有 n 个属性的关系 E,\(\rho_{x(A_1, \cdots, A_n)}(E)\) 会返回一个属性名字被改成 \(A_1, \cdots, A_n\) 的 E。

附加的操作

\(r \cap s = \{t | t \in r \land t \in s\} = r - (r - s)\)

自然连接

\(r \bowtie s = \Pi_{r 和 s 下的所有属性种类}(\sigma_{r 和 s 下相同的属性名的值都相同}(r \times s))\)

设 r 在模式 R 下,s 在模式 S 下,那么 \(r \bowtie s\) 是在模式 \(R \cup S\) 下的表。

\(r \div s = \{t | t \in \Pi_{R - S}(r) \land \forall_{u \in s} (tu \in r)\}\);或者说,得出来的商来自于 \(\Pi_{R - S}(r)\),里面的元组 t 和 s 里面任何元组的拼接结果都在 r 里面出现过。

赋值

符号是 \(\leftarrow\)。比如上面的除操作可以写成这样:

\(temp1 \leftarrow \Pi_{R-S}(r)\)

\(temp2 \leftarrow \Pi_{R-S}((temp1 \times s) - \Pi_{R-S, S}(r))\)(缺少了 s 的出现的元组)

\(result = temp1 - temp2\)

扩展的关系代数操作

广义投影

投影中包含数学表达式的投影。比如下面这个:

\(\Pi_{\mathrm{name, maxhp - hp}}(\mathrm{pokemon})\) 会返回所有宝可梦的名字和他们已经损失的 HP 量。

聚合函数和聚合操作

包括最大值、最小值、平均值等操作。

aggregate_operation_example.png

外连接

在自然连接的基础上,增加左外连接和右外连接,以及完全外连接。

遇到部分值冲突的时候,左外连接保留符号左边的表的数据,右外连接保留符号右边的表的数据。

这个时候完全外连接说“我全都要!”,那么只能在冲突元组的其他属性中的其中一个放上 null 了。

空值 null

对于 or:

  • (unknown or true) = true
  • (unknown or false) = unknown
  • (unknown or unknown) = unknown

对于 and:

  • (unknown and true) = unknown
  • (unknown and false) = false
  • (unknown and unknown) = unknown

对于 not:

  • not unknown = unknown

数据库的修改操作

删除可以表达为 \(r \leftarrow r - E\)

插入可以表达为 \(r \leftarrow r \cup E\)

更新可以表达为 \(r \leftarrow \Pi_{F_1, F_2, \cdots, F_l} (r)\),其中 \(F_i\) 对于哪些要更新的元组是相应的表达式。