概述
本书的前三章主要介绍了关系模型、数据库的组成、函数依赖、规范化过程以及使用SQL语句来操作数据库。四五章内容主要内容为一个数据库从无到有的过程,里面提到了很多重要的概念和方法,对我们实际的数据库开发工作有很大帮助。
数据库开发过程的三个步骤
- 需求分析
- 组件设计
- 实现
需求分析
需求分析过程本书提及的内容不多,不过这个过程在实际的开发中还是非常重要的,可能对我们程序开发人员来说,大部分需求分析的工作是产品经理完成的,不过我们要知道需求分析的目的:找出实体、实体的属性、属性的特性(数据类型、取值范围等)、实体间的关系和相关的业务规则。比如对于学生管理系统,显而易见的实体有:学生、课程等;学生的属性有姓名、性别等;性别只能是男或子女;学生可以有相应的学习课程;相应的业务规则如学生必须有相应的课程等。当然还有很重要的一点就是不断地与客户
交流,以更加有效的确定需求的完整性和准确性,这里的客户
是泛指。
实体-关系数据模型
在确定了具体需求后,并不是直接开始创建数据库和相关的数据表,而是需要将数据需求描述到数据模型中,一般创建数据模型采用的是实体-关系模型(E-R模型)
,全称为扩展的实体-关系模型(Extended entity-relationship model)。现在我们介绍这个模型中涉及到的一些概念和定义
实体
一般情况下,我们在需求分析时,会接触到很多实体的实例如学号为066的学生、化学实验课程、203教室等等…但是学生不止一个,课程和教室也不止为一,于是我们把所有的学生分组到STUDENT实体中、把所有教室分组到CLASSROOM实体中,STUDENT和CLASSROOM就是实体类,具体的一个学生或者教室我们称为实体实例,所以实体类有许多实体实例。需求分析时,我们就需要将实体实例抽象到实体类。
属性
属性是为了描述实体的特征,例如STUDENT实体,具有姓名、年龄、年级、性别及生日等属性,另外属性具有类型和特性,例如年龄是数字、生日是日期……而特性是指该属性是否必须,是否有默认值、取值是否有限制及其他约束,如性别只能是男或者女,而且是必须的。
标识符
实体具有属性外,还需要有一个标识符,标识符是给实体实例命名或者标识的属性,由一个或者多个属性组成标识符,所以标识符就是实体的一个属性或多个属性,多个属性组成的标识符称为复合标识符。标识符可以唯一也可以不唯一,这里的唯一和不唯一是指相同的标识符可以标识多少具体的实体实例。例如我们用姓名属性来标识学生,可能某一个名字只能标识一个学生,而对于同名的学生来说,这个姓名可以标识多个学生。
实体的表示
我们用一个矩形表示一个实体,根据需要用不同的层次表示一个实体:
图一列出了实体名、标识符和属性。图二只有实体名和标识符。而图三则只有实体名。
关系
实体是独立存在的,不同的实体存在各种各样的关系,实体之间也是通过关系来关联。关系分为关系类和关系实例,关系类反映实体类间的关系,关系实例放映了实体实例间的关系。关系类涉及多个实体,关系到的实体数目称为关系的度数,度数为二的关系最为常见,我们称为二元关系,度数为三的关系称为三元关系
二元关系的三种类型
- 一对一关系(1:1)
- 一对多关系(1:N)
- 多对多关系(M:N)
在1:1的关系中,一个实体实例仅和另一个实体实例相关联。图中的学生和座位的关系就是一对一,一个学生只对应一个座位,一个学生不能有多个座位;而某一个座位只能属于某一个学生,不能属于多个学生。
在1:N的关系中,一个学生有很多课本,但是某一个课本只属于某一个学生,不能被多个学生共用(不钻牛角尖)。在1:N的关系中,有时会用到父
和子
的术语,父实体是关系中表示“一”的实体,子实体是关系中表示“多”的实体。图中学生是父实体,课本是子实体。
最后的M:N关系中,一个学生有多个老师授课,一个老师也不止给一个学生授课,所以是多对多的关系。
最大基数
在图中菱形中的数字表示该方向实体的最大基数,表示在关系中该实体可以存在的最多实体实例数量。如果学生和课本的关系是1:15,则一个学生最多只能有15本课本,如10本、13本、15本,而不能超过15本,这就是最大基数。
最小基数
有最大基数就有最小基数,就是在关系中至少存在的实体实例,最小基数怎么表示呢?如下:
在关系的横线上加一个横条表示必须存在一个实体,在横线上添加一个椭圆表示可以不存在实体,最小基数为0表示实体参与是可选的,而最小基数是1的实体,则关系中实体参与是强制性的。对于一个学校,只要有一个学生,就必须和一个老师有关系,但是某个老师可以没有学生,可能他不并不授课(如后勤老师)。
实体-关系图
刚才我们已经接触到了实体关系图,但是都是最初的表示法,一般不再使用,而被大多数使用的是Information Engineering(IE),该模型用“鸟足”来显示多个实体的关系。如上面的学生老师关系:
左边符号表示STUDENT是多个可选的,右边的符号表示教室多个强制的。符号相关含义汇总:
图里的相关图形就表示了实体的最大基数和最小基数,离实体近的表示最大基数,离实体远的表示最小基数。
小结
现在我们可以从需求分析中提取出实体,并给它们添加属性,定义属性的特性和标识符,然后确定不同实体间的关系,然后还可以用图的方式把这些实体和关系都表示出来了。
一些特殊的实体
弱实体
弱实体是一种特殊的实体,它的存在取决于另一个实体,若另一个实体不存在,它就不存在,那么这个实体就是弱实体。比如在一个学校,有不同的教学楼,教学楼中有教室,但若教学楼不存在,则里面的教室也不存在,那么教室实体就依赖于教学楼实体,教室就是弱实体。非弱实体称为强实体。
ID依赖实体
ID依赖实体是弱实体的一种,一个ID依赖实体就是弱实体,但弱实体不一定都是ID依赖实体。观察下图:
一栋建筑中有多个房间,一个房间属于一栋建筑,我们在标识房间时,会使用到它所在的建筑,所以在房间的标识符中有建筑的标识符,我们就说房间ID依赖建筑。我们用圆角的方框表示ID依赖实体,并且是用实线连接两个实体,这种关系叫着标识关系,而用虚线连接的关系称为非标识关系。
非ID依赖的弱实体
在实际的情况中,可能存在标识不依赖其他实体的弱实体,例如,汽车设计模型实体和一个具体的汽车,他们在实际情况中都可以用不同的标识来标识,但是如果没有模型,汽车是不存在的,所以汽车也依赖于模型,它是弱实体。
如何区分弱实体和非弱实体
上面的我们可以看到,弱实体都存在一对多的关系,那么所有的一对多关系那个多的实体都是弱实体吗?答案是否定的,例如上面的的学生和课本就是一对多的关系,但是他们在逻辑上并不是依赖的,没有学生时,也可以有课本。所以判断弱实体一般是通过逻辑的存在关系判断。那么大家想一想,学生和成绩(各科)的关系中,成绩是弱实体吗?
子型实体
子型实体是另一种名为父型实体的特例。比如学生有可以分为大学生和中学生,学生是父型实体,大学生和中学生就是子型实体。
子型实体可以是排他的,也可以是包含的,排他的子型实体会有一个辨别器,如(a)图中的isGradStudent,它的表示图形中圈里有一个叉,表示排他的。排他和包含具体的含义分别是,子型实体是确定的某一个或者是可以使多个不同的子型。(a)中一个学生只能是毕业的或者没有毕业的;而(b)中一个学生可以参加多个兴趣俱乐部。
递归关系
不同实体之间存在关系,那么同一个实体之间也存在关系,例如一个学生可以和其他多个学生有关联(同班、同级、同社团等),我们把这个关系成为递归关系。递归关系也可能是1:1、1:N或者M:N的。
把数据模型转化为数据库设计
前面我们提到了很多概念,例如实体、属性、标识符和关系等……似乎他们就是我们数据库的表、列、主键和表之前的关系,那为什么却用不同的名词来定义呢。我是这样理解的,这些概念在不同的时期就有不同的称呼,例如实体,可能在数据模型阶段它只是一个粗糙的存在,它要称为表,还需要一定的步骤,所以就有了实体和数据表的不同称呼。接下来我们就来看看如何把数据模型转化为数据库设计,也就是数据模型成为具体的表、属性、主键等。
实体的转换
下面我们把一个STUDENT实体转化为一个数据表,前面的需求分析工作就不介绍了,我们从需求分析中建立了一个STUDENT实体:
接下来我们需要将它变为数据表,按照以下步骤进行:
- 首先,确定主键
- 列的特性:1)数据类型;2)空值状态;3)默认值;4)数据约束
- 验证规范化问题
- 是否需要反规范化
确认主键
在实体中StudentNumber为标识符,这里我们可以直接用它作为我们表的主键。确认主键的时候,我们有可能会考虑是否需要使用代理建,一般我们会从两个方面考虑是否需要代理建:
- 是否需要增加一个没有意义的字段
- 考虑合并的情况,如果在两个数据库中,同一个表都是用的代理建,则在合并时会代理键冲突
确认列的属性
验证规范化
我们按照第二章中规范化步骤来验证我们表是否规范
找出所有候选键
这个表中只有一个候选键StudentNumber
找出函数依赖
ZIP -> (City,State)
是否需要新增表
因为决定因子ZIP不是候选键,所以我们需要新增一个表,用ZIP作为主键:
ZIP(Zip, City, State)
STUDENT(StudentName, Name, Sex, ZIP)
在STUDENT表中ZIP作为外键,规范化过程结束
反规范化
有的时候,我们的规范化会过度,所以我们会考虑有的规范化是否需要。如果我们不把(Zip, City, State)这三个字段放在另外的表行吗?这就需要考虑插入、更新、删除三个方面的问题。首先,新增了一个城市(邮编),其他STUDENT中的这三个字段并不会受影响。然后是更新,现实情况,一个城市的邮编并不会更新,所以也没有影响。最后是删除,如果只有一个学生在某一个城市,删除这个学生后,这条城市信息就不存在了,不过这并不会影响我们,因为在我们学生系统中并没有需求单独的查某一个城市邮编的需求
所以我们的最终表为:
弱实体的表示
记住一条:不管是ID依赖的弱实体还是非ID依赖的弱实体,在转化为表的时候,主键中必须加入所依赖表的主键作为这个弱实体表的主键(一般会和这个弱实体的标识符一起构成复合主键)
关系的表示
前面我们讲了具体一个实体的变为表的过程,接下来我们说一个实体的关系怎么转化为表,实体的关系有三种,1:1、1:N、M:N,一般的我们是通过添加外键的方式表示关系,这三种关系的外键怎么添加呢?
1:1强实体关系
1:1的关系中我们可以将其中一个表的主键放在另一个表中作为外键即可。在上面的表示关系的图中,学生和座位是1:1的关系,那么我们在学生表中添加一个座位的主键作为外键,还是在座位表中添加一个学生的主键作为外键呢?考虑三个要素
- 查询的常见性
- 索引少出现
- 表的大小(可能连接查询)
首先,对于学生和座位,一般的情况我们是通过学生查位置,然后如果在StudentNumber上加了索引,我们就最好不要再把它放在其他表中作为外键了。最后,每一个学生肯定有一个座位,可能还要很多空的座位,所以座位表更大,连接查询时,通过学生表来连接会更快。综上,应该将座位的主键放在学生表中作为外键。
添加约束
1:1关系中有一点非常重要的就是约束,因为是1:1,所以一个表的主键在另一个表中最多出现一次,所以我们还必须将那个作为外键的列约束为唯一(UNIQUE)
1:N强实体关系
1:N的关系是最固定的,在上图中1:N关系中的学生和课本实体,我们提到了父实体和子实体,一般情况下,我们将父表中的主键放在字表中作为外键,这很容易理解为什么这么做。1:N的关系中,我们就不能约束外键唯一了,外键可以在字表中出现多次
M:N强实体关系
M:N关系中,我们不能按照上面的方式将其中一个表的主键作为另一个表的外键了,这样是行不通的,M:N关系的做法是:新增一个表,称为交集表。如学生和班级的关系用表表示:
新表的主键是两个表的主键共同组成的,所以新表是ID依赖的。
关联关系和混合实体
前面提到过标识关系和非标识关系,是在两个实体间的关系,对于图中的三个实体,如果我们再在STUDENT_CLASS表中新增一个属性,变为:
我们就称这种关系为关联关系,虽然STUDENT_CLASS仍依赖STUDENT和CLASS,但是新增字段后,它有了独立的数据,所以它是关联关系。那什么又是混合实体呢?如下图:
一个实体依赖其中一个实体,而不依赖另外的一个,这种模式就是混合实体模式。
子型实体的表示
很简单,将父型实体的主键作为子型实体的主键和外键即可。
递归关系的表示
在递归关系中,也有1:1、1:N、M:N三种关系:
1:1的递归关系
新增一个列,把被资助(上图的1:1)的主键放入该列,并且该列唯一
1:N的递归关系
新增一个列,把介绍人(上图的1:N)的主键放入该列,但是不唯一
M:N的递归关系
新增一个表,用医生和患者医生的ID作为复合主键
递归关系的查询
递归关系的表虽然有关系,但是只存在一个表,如果我们需要查询相关数据怎么办,这里新介绍一种查询数据的办法,先在我们之前3章中用到的表里新增一列LeadNumber:
ALTER TABLE EMPLOYEE ADD LeadNumber INT;
将表中EmployeeNumber为3的员工领导设为EmployeeNumber为2的员工:
UPDATE EMPLOYEE SET LeadNumber = 2 WHERE EmployeeNumber = 3;
查询关系:
SELECT * FROM EMPLOYEE A, EMPLOYEE B WHERE A.LeadNumber = B.EmployeeNumber;
这样就查出了对应的领导,同样我们可以查询谁为谁的领导:
SELECT * FROM EMPLOYEE A, EMPLOYEE B WHERE A.EmployeeNumber = B.LeadNumber;