《1》关系模型介绍
• 在商用数据处理应用中,关系模型已经成为当今主要的数据模型。之所以占据主要位置,是因为和早期的数据模型如网络模型或层次模型相比,关系模型以其简易性简化了编程者的工作。
2.1 关系数据库的结构
• 关系数据库由表( table )的集合构成,毎个表有唯一的名字。
• 例如,图2-1中的 instructor 表记录了有关教师的信息,它有四个列首: ID 、 name 、 dept _ name 和 salary 。该表中每一行记录了一位教师的信息,包括该教师的 ID 、 name 、 dept _ name 以及 salary 。
• 类似地,图2-2中的 course 表存放了关于课程的信息,包括每门课程的 course _ id 、 title 、 dept _ name 和 credits 。注意,每位教师通过 ID 列的取值进行标识,而每门课程则通过 course _ id 列的取值来标识。
• 图2-3给出的第三个表是 prereq ,它存放了每门课程的先修课程信息。该表具有 course _ id 和 prereq _ id 两列,每一行由一个课程对组成,这个课程对表示了第二门课程是第一门课程的先修课。由此, prereq 表中的每行表示了两门课程之间的联系:其中一门课程是另一门课程的先修课。作为另一个例子,我们考察 instructor 表,表中的行可被认为是代表了从一个特定的 ID 到相应的 name 、 dept _ name 和 salary 值之间的联系。
• 一般说来,表中一行代表了一组值之间的一种联系。由于一个表就是这种联系的一个集合,表这个概念和数学上的关系这个概念是密切相关的,这也正是关系数据模型名称的由来。
• 在数学术语中,元组( tuple )只是一组值的序列(或列表)。在 n 个值之间的一种联系可以在数学上用关于这些值的一个 n 元组( n - tuple )来表示,换言之, n 元组就是一个有 n 个值的元组,它对应于表中的一行。
• 这样,在关系模型的术语中,关系( relation )用来指代表,而元组 tuple )用长指代行类似地,属性( attribute )指代的是表中的列。
• 考察图2-1,我们可以看出 instructor 关系有四个属性: ID 、 name 、 depi _ name 和 salary 。
• 我们用关系实例( relation instance )这个术语来表示一个关系的特定实例,也就是所包含的一组特定的行。图2-1所示的 instructor 的实例有12个元组。对应于12个教师。
• 本章我们将使用多个不同的关系来说明作为关系数据模型基础的各种概念。这些关系代表一个大学的一部分。它们并没有包含真实的大学数据库中的所有数据,这主要是为了简化表示。
• 由于关系是元组集合,所以元组在关系中出现的顺序是无关紧要的。因此,无论关系中的元组是像图2-1那样被排序后列出,还是像图2-4那样无序的,都没有关系;在上述两图中的关系是一样的,因为它们具有同样的元组集合。为便于说明,当我们在显示关系时,大多数情况下都按其第一个属性排序。
图2-4 instructor 关系的无序显示
• 对于关系的每个属性,都存在一个允许取值的集合,称为该属性的域( domain )。这样 instructor 关系的 salary 属性的域就是所有可能的工资值的集合,而 name 属性的域是所有可能的教师名字的集合。
• 我们要求对所有关系 r 而言, r 的所有属性的域都是原子的。如果域中元素被看作是不可再分的单元,则域是原子的( atomic )。例如,假设 instructor 表上有一个属性 phone _ number ,它存放教师的一组联系电话号码。那么 phone _ number 的域就不是原子的,因为其中的元素是一组电话号码,是可以被再分为单个电话号码这样的子成分的。
• 重要的问题不在于域本身是什么,而在于我们怎样在数据库中使用域中元素。现在假设 phone _ number 属性存放单个电话号码。即便如此,如果我们把电话号码的属性值拆分成国家编号、地区编号以及本地号码,那么我们还是把它作为非原子值来对待。如果我们把电话号码当作不可分割的单元,那么phone_number属性才会有原子的域。
• 空( null )值是一个特殊的值,表示值未知或不存在。如前所述,如果我们在关系 instructor 中包括属性 phone _ number ,则可能某教师根本没有电话号码,或者电话号码未提供。这时我们就只能使用空值来强调该值未知或不存在。以后我们会看到,空值给数据库访问和更新带来很多困难,因此应尽量避免使用空值。
2.2数据库模式
• 当我们谈论数据库时,我们必须区分数据库模式( database schema )和数据库实例( database instance ),前者是数据库的逻辑设计,后者是给定时刻数据库中数据的一个快照。
• 关系的概念对应于程序设计语言中变量的概念,而关系模式( relation schema )的概念对应于程序设计语言中类型定义的概念。
• 一般说来,关系模式由属性序列及各属性对应域组成。
• 关系实例的概念对应于程序设计语言中变量的值的概念。给定变量的值可能随时间发生变化;类似地,当关系被更新时,关系实例的内容也随时间发生了变化。相反,关系的模式是不常变化的。
• 尽管知道关系模式和关系实例的区别非常重要,我们常常使用同一个名字,比如 instructor ,既指代模式,也指代实例。在需要的时候,我们会显示地指明模式或实例。例如“ instructor 模式”或“ instructor 关系的一个实例”然而,在模式或实例的含义清楚的情况下,我们就简单地使用关系的名字。
• 考察图2-5中的 department 关系,该关系的模式是:
department ( dept _ name , building , budget )
• 请注意属性 dept _ name 既出现在 instructor 模式中,又出现在 department 模式中。这样的重复并不是一种巧合。实际上,在关系模式中使用相同属性正是将不同关系的元组联系起来的一种方法。
• 例如,假设我们希望找出在 Watson 大楼工作的所有教师的相关信息。我们首先在 department 关系中找出所有位于 Watson 的系的 dept _ name 。接着,对毎一个这样的系,我们在 instructor 关系中找出与 dept _ name 对应的教师信息。
图2-5 department 关系
我们继续看大学数据库的例子。
• 大学里的毎门课程可能要讲授多次,可以在不同学期授课,甚至可能在同一个学期授课。我们需
要一个关系来描述每次课的授课情况或分段情况。该关系模式为:
section ( course _ id , sec _ id , semester , year , building , room _ number , time _ slot _ id )
• 图2-6给出了 section 关系的一个示例。
我们需要一个关系来描述教师和他们所讲授的课程段之间的联系。描述此联系的关系模式是:
teaches ( ID , course _ id , sec _ id , semester , year )
图2-7给出了 teaches 关系的一个示例。
• 在一个真正的大学数据库中还维护了更多的关系。除了我们已经列出的这些关系: instructor 、 department 、 course 、 section 、 prereq 和 teaches ,在本书中我们还要使用下列关系:
• student ( ID , name , dept _ name , tot _ cred )
• advisor ( s _ id , i _ id )
• takes ( ID , course _ id , sec _ id , semester , year , grade )
• classroom ( building , room _ number , capacity )
• time _ slot ( time _ slot _ id , day , start _ time , end _ time )
图2-7 teaches 关系
2.3码
• 我们必须有一种能区分给定关系中的不同元组的方法。这用它们的属性来表明。也就是说,一个元组的属性值必须是能够唯一区分元组的。换句话说,一个关系中没有两个元组在所有属性上的取值都相同。
• 超码( superkey )是一个或多个属性的集合,这些属性的组合可以使我们在一个关系中唯一地标识一个元组。例如, instructor 关系的 ID 属性足以将不同的教师元组区分开来,因此, ID 是一个超码。另一方面, instructor 的 name 属性却不是一个超码,因为几个教师可能同名。
• 形式化地描述,设 R 表示关系 r 模式中的属性集合。如果我们说 R 的一个子集 K 是 r 的一个超码,则限制了关系 r 中任意两个不同元组不会在 K 的所有属性上取值完全相等,即如果t1和t2在 r 中且t1≠ t2,则t1. K ≠ t2.K 。
• 超码中可能包含无关紧要的属性。例如, ID 和 name 的组合是关系 instructor 的一个超码。如果 K 是一个超码,那么 K 的任意超集也是超码。我们通常只对这样的一些超码感兴趣,它们的任意真子集都不能成为超码。这样的最小超码称为候选码( candidate key )。
• 几个不同的属性集都可以做候选码的情况是存在的。假设 name 和 dept _ name 的组合足以区分 instructor 关系的各个成员,那么{ID}和 {name , dept _ name }都是候选码。虽然属性 ID 和 name 一起能区分 instructor 元组,但它们的组合{ ID , name }并不能成为候选码,因为单独的属性 ID 已是候选码。
• 我们用主码( primary key )这个术语来代表被数据库设计者选中的、主要用来在一个关系中区分不同元组的候选码。码(不论是主码、候选码或超码)是整个关系的一种性质,而不是单个元组的性质。关系中的任意两个不同的元组都不允许同时在码属性上具有相同的值。码的指定代表了被建模的事物在现实世界中的约束。
• 主码的选择必须慎重。正如我们所注意到的那样,人名显然是不足以作主码的,因为可能有多个人重名。在美国,人的社会保障号可以作候选码。而非美国居民通常不具有社会保障号,所以跨国企业必须设置他们自己的唯一标识符。另外也可以使用另一些属性的唯一组合作为码。
• 主码应该选择那些值从不或极少变化的属性。例如,一个人的地址就不应该作为主码的一部分,因为它很可能变化。另一方面,社会保障号却可以保证决不变化。企业产生的唯一标识符通常不变,除非两个企业合并了,这种情况下可能在两个公司中会使用相同的标识符,因此需要重新分配标识符以确保其唯一性。
• 习惯上把一个关系模式的主码属性列在其他属性前面;例如, department 中的 dept _ name 属性最先列出,因为它是主码。主码属性还加上了下划线。
• 一个关系模式(如 r1 ,)可能在它的属性中包括另一个关系模式(如 r2)的主码。这个属性在 r1 上称作参照 r2 的外码( foreign key )。关系 r1也称为外码依赖的参照关系( referencing relation), r2 叫做外码的被参照关系( referenced relation )。
• 例如, instructor 中的 dept _ name 属性在 instructor 上是外码,它参照 department ,因为 dept _ name 是 department 的主码。在任意的数据库实例中,从 instructor 关系中任取一个元组,比如ta,在 department 关系中必定存在某个元组,比如tb,使得tb 在 dept _ name 属性上的取值与tb 在主码 dept _ name 上的取值相同。
• 现在考察 section 和 teaches 关系。如下需求是合理的:如果一门课程是分段授课的,那么它必须至少由一位教师来讲授;当然它可能由不止一位教师来讲授。为了施加这种约束,我们需要保证如果一个特定的( course _ id , sec _ id , semester , year )组合出现在 section 中,那么该组合也必须出现在 teaches 中。可是,这组值并不构成 teaches 的主码,因为不止一位教师可能讲授同一个这样的课程段。其结果是,我们不能声明从 section 到 teaches 的外码约束(尽管我们可以在相反的方向上声明从 teaches 到 section 的外码约束)。
• 从 section 到 teaches 的约束是参照完整性约束( referential integrity constraint )的一个例子。参照完整性约束要求在参照关系中任意元组在特定属性上的取值必然等于被参照关系中某个元组在特定属性上的取值。