2.关系模型
2.1 关系数据库的结构
关系数据库由表(table)
的集合构成,一般来说,表中的一行代表了一组值之间的一种联系(relationship)
,由于一个表就是这种联系的一个集合,表这个概念和数学上的关系这个概念是密切相关的
在数学术语中,元组(tuple)
只是一组值的序列(或者列表)。在n个值之间的一种联系在数学上用关于这些值的一个n元组(n-tuple)
来表示,换言之,n元组就是一个有n个值的元组,它对应于表中的一行
关系(relation)
用来指表,而元组(tuple)
指代行,类似地,属性(attribute)
指代的是表中的列
我们用关系实例(relation instance)
这个属于来表示一个关系的特定实例,也就是所包含的一组特定的行
由于关系是元组的集合
,所以元组在关系中出现的顺序是无关紧要的,因为他们具有同样的元组集合。对于关系的每个属性,都存在一个允许取值的集合,称为该属性的域(domain)
。
我们要求对所有的关系r
而言,r的所有属性的域
都是原子的。如果域中元素被看作是不可再分的单元,则域是原子的(atomic)
。啥叫原子性的,其实就是属性不可以再被划分为再小的属性单元,比如说像address
这种,可以分成省份,城市啥啥啥的,这种可以再分的这种情况就是不原子性的,然而这是相对的,根据数据库定义人员对这个字段的定义以及实际业务开发中对这个字段的利用程度而言。
空(null)
值是一个特殊的值,表示值位置或者不存在。空值会给数据库访问和更新带来很多困难,因此应该尽量避免使用空值。
2.2 数据库模式
当我们讨论数据库时,我们必须区分数据库模式(database schema)
和数据库实例(database instance)
,前者是关于数据库的逻辑设计而后者是给定时刻数据库中数据的一个快照
快照:一个暂存的实例,是在特定的时空下表现出来的一个数据库状态的体现。
关系的概念对应于程序设计语言中变量的概念,而关系模式的概念对应于程序设计语言中类型定义的概念
2.3 码
我们必须有一种能区分给给定关系中的不同元组的方法。这用它们的属性来表明,也就是说,一个元组的属性值必须是能够唯一区分元组
的。
超码(superkey)
:是一个多个属性的集合
,这些属性的组合可以使得我们在一个关系中唯一地表示一个元组
形式化地描述,设R
表示关系r
模式中的属性集合。如果我们说R
的一个子集K
是r
的一个超码,则限制了关系r中任意两不同元组不会在K
的所有属性上取值完全相等。
超码
中可能包含无关紧要的属性。如果K是一个超码,那么K的任意超集也是超码。我们通常研究这样的超码(它们任意的真子集都不能称为超码)
。这样的最小超码称为候选码(candidate key)
我们用主码(primary key)
这个属于来代表被数据库设计者选中的、主要用来在一个关系中区分不同元素的候选码。码(不论是主码、候选码、还是超码)都是整个关系的一种性质,而不是单个元组的性质。
一个关系模式(如r1)
可能在它的属性各种包括另一个关系模式(如r2)
的主键。这个属性在r1
上称作参照r2
的外键。关系r1
也称为外键依赖的参照关系(referencing relation)
,r2
被叫做外码的被参照关系(referenced relation)
参照完整性约束(referential integrity constraint)
:参照完整性约束要求在参照关系中任意元组在特定属性上的取值必然等于被参照关系中某个元组在特定属性上的取值
2.4 模式图
一个含有主码和外码依赖的数据库模式可以用模式图(schema diagram)
来表示
2.5 关系查询语言
查询语言(query language)
:是用户用来从数据库只能请求获取信息的语言
过程化语言(procedural language)
中,用户指导数据库执行一系列操作以计算出所需结果。
非过程化语言(noprocedural language)
中,用户只需要描述所需信息。而不用给出获取该信息的具体过程
实际使用的查询语言既包含过程化方式的成分,又包含非过程化的成分。
关系代数和关系演算的两种形式:元组演算和域关系演算,关系代数包括一个运算的集合,这些运算以一个或者两个关系为输入,产生一个新的关系为结果。关系演算使用谓词逻辑来定义所需的结果,但不需要给出获取结果的特定代数过程
接下来对这几种操作进行理解
2.5.1 选择语句
选择(select)
:运算选出满足给定谓词的元组,用小写的希腊字母(sigma)
来表示选择,用法实例:
$$\sigma _{dept_name=”Physics”}(instructor)$$可以选出具有Physics
属性的所有instructor
,本质是就是过滤出一张新的表
通常,我们允许在选择谓词中进行比较,使用的是=,≠ ,<,≤,>和≥
。另外,我们可以用连词and(^)、or(V)和not
来将多次谓词合并为一个较大的谓词。
2.5.2 投影语句
投影(project)
:是一元运算,它返回作为参数的关系,但把某些属性排除在外,由于关系是一个集合,所以所有重复行均被去除。运算结果是一个集合,使用示例
$$\Pi _{ID,name,salary}(instructor)$$
2.5.3 笛卡尔积运算语句
用X
表示的笛卡尔积(Cartesian-product)
运算使得我们可以将任意两个关系的信息组合在一起。我们将关系r1Xr2
,笛卡尔积的结果简单来说就是从r1中取一个元组然后和r2的所有元组做组合,组合完毕后填入r3,r3含有r1和r2的所有属性,依次将r1剩余的元素与r2的全部元素组合,最终就是笛卡尔积的结果
例如:我们计算r=instructor X teaches
的关系的模式为例,我们可以区别instructor.ID和teaches.ID
这样的方式来区别两个模式相同的属性名,对那些只在两个关系模式之一出现的属性,我们通常省略其关系名前缀。这样的简化不会导致任意歧义。
这个命名规则作为笛卡尔积运算参数的关系名字必须不同。
当某个关系需要与自身做笛卡尔积的时候,我们必须给关系一个名字以引用其属性(一个关键技术是更名运算)
2.5.4 集合并语句
并(Union)
,当我们需要出现在这两个集合之一的或同时出现在这两个集合中的所有课程段的ID
,可以通过并运算来获得这些数据,由于关系是集合,所以如果有重复值的,那么就会被选出一个元组,只留下一个。运算结果也是一个集合。
为了使得并运算有意义,我们要求一下的两个提交必须同时成立
关系r和s
必须是同元的,也就是说它们的属性数目必须相同- 对于所有的
i,r
的第i
个属性和域必须和s
的第i
个属性的域相同
说人话就是,两个集合的属性必须一模一样。
2.5.5 集合差语句
用-
表示集合差(set-different)
运算使得我们可以找出在一个关系中而不在另一个关系中的那些元组
,运算结果也是一个集合
同样的,为了使得差运算有意义,我们要求一下的两个提交必须同时成立
关系r和s
必须是同元的,也就是说它们的属性数目必须相同- 对于所有的
i,r
的第i
个属性和域必须和s
的第i
个属性的域相同
2.5.6 集合交语句
集合交(intersection)
相当于是r-(r-s)
2.5.7 更名运算
我们可以通过小写希腊字母$$\rho$$表示更名(rename)
运算来完成这一任务,对于给定的关系代数表达式E,表达式$$\rho_{x}(E)$$它将返回表达式E的结果,并把名字x赋值给了他
另一种形式是:我们假设关系代数表达式E
是n
元的,则表达式
$$\rho_{x}(A_1,A_2…,A_n)$$
例子:我们以查询找出大学里的最高工资
作为例子来演示更名运算,策略:首先计算出一个由非最高工资组成的临时关系
,然后计算关系(教职工表的的薪水投影集合)与刚才算出的临时关系之间的集合差
- 步骤一:为了计算该临时关系,我们需要比较所有工资的值。要做这一的比较,可以通过就是计算笛卡尔积
instructor X instructor
并构造一个选择来比较任意两个出现在同一元组中的工资。我们的首要任务是设计一种机制来区别两个salary
属性,我们将使用更名运算来改变其中一个对教师关系进行引用的名字,这样就可以无歧义地引用这个关系
现在可以把非最高工资构成的临时关系写成:
$$\Pi {A_1.salary}(\sigma{A_1.salary<A_2.salary(\rho_{A_1}(instructor) \times \rho_{A_2}(instructor)})$$
这个怎么理解咧?我们来个例子哈,这个是初始的薪水表
salary |
---|
100 |
200 |
300 |
然后我们对他重名后做笛卡尔积,会得到
A1.salary | A2.salary |
---|---|
100 | 100 |
100 | 200 |
100 | 300 |
200 | 100 |
200 | 200 |
200 | 300 |
300 | 100 |
300 | 200 |
300 | 300 |
然后我们选出那些A1.salary<A2.salary
的元组,并且投影出来
A1.salary |
---|
100 |
200 |
300没有被选上的原因是300>100,300>200,300=300,这就是最大值的特性
然后咱们用原始薪水表去和这个表作差,就是一个仅含有一个元素的薪水表,这样就查到了最大值
2.5.8 自然连接语句
自然连接(join)
提供了一种手段,它可以提供一种选择规则,要求笛卡尔积的两个关系在所有属性上的值完全一致。具体流程:它先对两个参数计算笛卡尔积,然后基于两个关系模式都出现的属性上的相等性进行选择,最后还要去除重复属性。
一种属性相同当然是可以理解的,如果是两个属性都相同,那么它就会查找两个元素,转化为sql语句如下
select *
from table1
natural join table2
- 单个属性相同:table1(A,B) table2(A,C)
select t1.*
from table1 t1
inner join table2 t2
on t1.a = t2.a
- 多个属性相同:table1(A,B,D) table2(A,B,C)
select t1.*
from table1 t1
inner join table2 t2
on t1.a = t2.a and t1.b = t2.b
2.6 作业解析(个人理解,不一定对)
表结果
employee(ID,person_name,street,city)
works(person_name,company_name,salary)
company(company_name,city)
6.4
a:Find the ID and name of each employee who does not work for “BigBank”
要找的是那些不为BigBank
工作的员工的ID和name
- 第一种思路
直接查找,我们可以通过限定works中company_name的名字,然后你会得到一个其工作公司不是BigBank
的works的过滤后集合,然后把这个集合拿出来做投影,投影出只有person_name
这个信息的集合。
然后把这个只有person_name
的集合去和employee
做自然连接(因为需要ID,只有employee有ID),连接完毕后再投影即可
写出表达式如下
$$\Pi {ID,person_name}(\Pi{person_name}(\sigma _{company_name≠”BigBank”}(works)) \bowtie employee)$$
注意,这个解法实际上是错误的,这是因为我们忽略掉了如果
works
中如果一位员工不止为一个公司工作的话,比如说john
同时是Acompany
和BigBank
的员工,但是这时候关于Acompany
的那一个元组就会导致John
被加入到我们的结果元组里面去,这个结果是错误的。
- 第二种思路
既然要找不为它工作,那我找出为它工作的,然后用全部减去为它工作的不就好了吗
$$\Pi_{ID,person_name}(employee) - \Pi {ID,person_name}(\Pi{person_name}(\sigma _{company_name=”BigBank”}(works)) \bowtie employee)$$
=
这个条件严格保证了员工为BigBank
工作,在这样的情况下,只要用差集的办法就能够求出相关的员工
b:简单来说就是找一个最大值
找最大值的方法在2.5.7中已经解析过了,这里直接把结论套进去即可
首先写出最大值
$$ \Pi {salary}(works)- \Pi {A_1.salary}(\sigma{A_1.salary<A_2.salary(\rho{A_1}(works) \times \rho_{A_2}(works)})$$
然后把这个最大值拿去和works表做自然连接,得到一个具有最大薪水的员工表
$$(\Pi {salary}(works) -\Pi {A_1.salary}(\sigma{A_1.salary<A_2.salary(\rho{A_1}(works) \times \rho_{A_2}(works)})\bowtie works$$
接着就是将这个表投影成只有person_name
的属性,再次和employee表做自然连接,连接完毕之后和employee做外连接,这时候会检索salary和person_name均相同的元组信息,答案如下:
注意当涉及到多个自然连接的时候,最好用括号指明顺序
$$\Pi {ID,person_name}(((\Pi {salary}(works) -\Pi {A_1.salary}(\sigma{A_1.salary<A_2.salary(\rho{A_1}(works) \times \rho{A_2}(works)})\bowtie works)\bowtie employee)$$
6.10
a:这道题和6.4的a重复了,可以这样写
$$\Pi {ID,person_name}(\Pi{person_name}(\sigma _{company_name=”BigBank”}(works)) \bowtie employee)$$
b:这道题主要就是要找到与BigBank
工作的所有员工的person_name,然后把这个person_name拿去和works、city做自然连接
$$\Pi {ID,person_name,city}(\Pi{person_name,city}(\sigma _{company_name=”BigBank”}(works)) \bowtie employee)$$
c:这道题和上道题的是类似的,只是添加了一个薪水>10000的条件,然后多查一个属性
$$\Pi {ID,person_name,city,street}(\Pi{person_name,city}(\sigma _{company_name=”BigBank” \wedge salary≥10000}(works)) \bowtie employee)$$
d:这道题要求找出ID和name,满足的条件是该员工的居住地和公司所在地是一样的
注意:坑点在于,如果这个员工没有为任何一家公司工作,那么就不算作满足题目的条件,所以需要三个表一起自然连接
$$\Pi_{ID,person_name}((company\bowtie works)\bowtie employee) $$