数据库扫盲贴 —— 快速设计一个关系型数据库

1,565 阅读11分钟

写在前面

这篇文章属于扫盲贴,如果你是一个移动端开发,平时对数据库接触较少,碰到复杂的存储关系就不知道该怎么办的话,建议你通读这篇文章。

文章中有一些比较书面化的知识,也许真正实战中用不到,但是学一学也没有什么坏处。

这篇文档主要介绍一些数据库的基础知识、范式、ER图等。
最后也会介绍一个通用流程,方便快捷的设计与建立表。通用设计部分在本文最后,标题为:E-R图设计。
本文只介绍关系型数据库,并且不涉及语句,如果有时间的话后面会把语句、复杂查询、存储过程等一并讲解。

实体(表)

实体代表了一个包含n个属性的集合,例如:人有姓名、身份证号、性别等,人就是一个实体。

关系(表)

关系表中存储了两个实体的关系,最简单的关系有三列:实体1,实体2,关联关系实体。例如:人A,人B,夫妻关系。

关系型数据库中关系通常分为三种:

  1. 1 - N(一对多)
  2. N - 1(多对一)
  3. N - N(多对多)

通常N - N型的关系需要建立关系表,后续会具体讲解

主键(主码 / 主关键字 / Primary Key)

主键可以是表中的一个或多个字段,唯一的标示表中的某一条数据,或者说代表了一个实体。

作用:

  1. 保证实体完整性。
  2. 加快数据库的操作速度(主键列默认增加索引)。
  3. 主键不允许重复,可以避免数据重复问题。
  4. 默认按照主键顺序排序。

外键(外码 / 外关键字 / Foreign Key)

定义:设F是基本关系R中的一个(组)属性,但不是该关系的键,Ks是基本关系S的主键,如果F与Ks对应,则称F是R的外键。

根据定义,我们可以知道外键仅仅表示的是关系的映射,不一定要用foreign key关键字来定义,通常我们也认可这种做法,更加灵活。

但是 foreign key 关键字可以确保数据一致性,当你想删除某个实体时,若该实体的主键存在于其他表中的外键时,会无法删除,必须同步,这样可以避免脏数据。通常有以下两种做法。

阻止执行

  • 从表插入数据,其外键值若不存在于主表的主键,阻止插入。
  • 从表修改外键值,若不存在于主表的主键,阻止修改。
  • 主表删除行,若主键存在于从表外键,阻止删除。
  • 主表修改主键,若主键存在于从表外键,阻止修改。

级联执行

  • 主表删除行,连带从表一起删除。
  • 主表修改主键值,连带从表的外键值一起修改。
  • 通过拒绝同一事务在从表中的标志与主表不一致来实现与主表中的标志一致。

范式(部分理论内容不感兴趣的话可以跳过)

定义:符合某一种级别的关系模式的集合,表示一个关系内部各属性之间的联系的合理化程度。

范式分为:1NF, 2NF, 3NF, BCNF, 4NF, 5NF

高一级别的范式必定符合低一级别的范式。设计关系型数据库时,最多考虑到BCNF,通常我们客户端在3NF或以下就可以了。

这里我们只讲到3NF,BCNF及以上,我们以后有机会再讲。

第一范式(1NF)

定义:符合1NF中的每个属性都不可再分

例如下图,就不符合1NF:


事实上,在任何RDBMS(Relational Database Management System)中,已经存在的表都符合1NF。我们必须如下设计:


显然的是,在1NF中会存在大量的冗余数据与问题,我们来看下面一个数据库,会存在哪些问题:


表[1]
  • 问题一:数据冗余过大 学号、姓名、系名、系主任这些数据重复多次。
  • 问题二: 插入异常 如果院系已经开设,但未分配系主任,若该列不能为空,则无法插入。
  • 问题三: 删除异常 如果想要将某个系中的学生记录都删除,那么系主任与该系的数据也一并删除了,这是不对的。
  • 问题四:修改异常 同上

第二范式(2NF)

严格定义这里不展开了,我们来看一下第二范式和第一范式之间的区别是什么。

2NF在1NF的基础上消除了非主属性对于码的部分函数依赖。

翻译成人话:若有(主属性+ 非主属性)才能唯一确定的属性,我们将该关系消除。

接来下先介绍几个概念。

函数依赖

简单理解:如果确定X,则一定能确定Y,那么称Y函数依赖于X,写作 X → Y。非常类似于函数中的y = f(x),其中y的值由x确定,x的值无法由y唯一确定。

例如在上述例子中,姓名函数依赖于学号

完全函数依赖

若 X → Y,Z ⊊ Y(Z是Y的真子集),X → Z不成立,则称Y 完全函数依赖于 X

例如:学号 F→ 姓名,(课名,学号)F→ 成绩(这里的符号不好打,符号表示如下)


部分函数依赖

若 X → Y,且Y 不完全函数依赖于 X,则称 Y 部分函数依赖于 X。

例如:X → Y, Z → Y,这里的关系称为不完全依赖,符号表示如下:


传递函数依赖

在Y不包含X,且X不函数依赖于Y这个前提下,X → Y,Y → Z,则称Z 传递函数依赖于 X


设K为某表中的一个属性(组),除K之外的所有属性都完全函数依赖于K,则称K为候选码,简称码。

简而言之,K确定了,其余属性都能确定了,那么K就是码。一张表中可以有超过一个码。

非主属性

包含在任何一个码中的属性称为主属性。

第二范式(2NF)

上述几个概念都介绍完以后,我们就可以来使用非常复杂的步骤来看2NF了。

  1. 找出数据库中所有的码。
  2. 根据步骤1找出所有的主属性与非主属性。
  3. 查看是否存在非主属性对码的部分函数依赖。

上述步骤可以转换为:

  1. 查看所有单一属性,判断是否当它的值确定吼,剩下所有属性值都能确定。
  2. 查看包含两个属性的属性组,当他的值确定了,是否剩下所有属性值都能确定。
  3. 增加属性组属性个数,重复2。

这里有个简单方法,就是如果A是码,那么其余包含了A的属性组,例如(A, B), (A, B, C)等就都不是码了,因为码的要求里有一个完全函数依赖。

在表[1]中,只有一个码,就是(学号,课名),那么主属性为:学号、课名;非主属性为:姓名、系名、系主任、分数。然后查看非主属性对码的部分函数依赖。

  • (学号,课名) P→ 姓名,有 学号 → 姓名。
  • (学号,课名)P→ 系名,有 学号 → 系名。
  • (学号,课名)P→ 系主任,有 学号 → 系主任

所以表[1]只符合1NF,不符合2NF,我们必须要消除这些部分函数依赖,只有一个办法就是将大数据表拆分成多个更小的数据表,拆分过程中也要达到更高一级的范式要求。

这个过程叫做模式分解。模式分解的方法不唯一,这里提供一种:

  • 选课表:学号,课名,分数
  • 学生表:学号,姓名,系名,系主任

这样拆分的话,可以看出都符合2NF的要求了,可以尝试自己验证一下。

拆分之后的表如下:


选课表 表[2]


学生表 表[2]



我们看看这样对于1NF是否有一些改进了

  1. 如果Zoe转到法律系,我们只需要修改学生表的一条记录即可 有改进
  2. 系名,学生名,系主任,冗余数据减少 有改进
  3. 删除学生信息时,系信息丢失 无改进
  4. 插入无系主任的新系,无法插入 无改进

所以可以看出,仅仅符合第二范式,无法达到我们的需求,我们还需要改进为符合第三范式的要求。

第三范式(3NF)

3NF在2NF的基础上,消除了非主属性对于码的传递依赖。3NF的思考过程与2NF相似的地方我就简单略过了。

知道了概念以后让我们看看 表[2] 是否符合第三范式。

  1. 选课表:码为(学号,课名),非主属性只有成绩,因此不可能存在传递函数依赖,因此符合3NF。
  2. 学生表:码为 (学号),学号 → 系名,系名 → 系主任,因此 学号 T→ 系主任,存在传递函数依赖,不符合3NF要求。

那么我们需要解决这里的传递函数依赖,将表进一步拆分:


学生表 表[3]


学生表 表[3]


院系表 表[3]

这样设计后,表[3]就是符合3NF的了,大家可以自行验证是否符合3NF。

E-R图设计

E-R图可以帮助我们理清实体,关系,属性之间的联系,往往拿到一个需求的时候会先设计E-R图,当E-R图设计完成后,基本上表结构也就可以设计出来了。

在E-R图设计中有,不同形状代表着不同的含义,分为三种:

  1. 实体(名词):矩形
  2. 属性(名词):椭圆形
  3. 关系(动词):菱形

对于上述学生的例子,我们稍微扩充一下,来看一下它的E-R图是什么样的:


我们给课程也加了一些额外的属性,例如课程号,院系也是。绘制ER图的流程如下:

  1. 确定实体,在这里就是 学生、课程、院系,用矩形框表示。
  2. 确定实体的属性,用椭圆框表示,并且跟相应的实体相连。
  3. 确定实体之间的动词,这里是 有、选修。
  4. 确定实体动词之间的对应关系。例如:1个学生可以有1个院系,1个院系可以有n个学生,因此学生与院系的关系是 n - 1。

这里会让人困的点在于怎么选取动词,例如为什么不能 院系 -- 有 -- 院系名 。这里解释一下,这种1 - 1的关系,我们通常将它收起在一张表里,作为其中一个实体的属性即可。

根据 E-R图 建表

当我们画好E-R图以后,就可以按照如下规则建表。

  1. 根据属性,建立实体表。
  2. 根据对应关系建立,关系表,按照如下规则:
  • 1 - n 关系,将 1 的那个实体表中的主键,作为 n 实体表的外键。不用生成关系表。例如:学生表增加一个 院系号 属性,并外键连接到院系表的主键。
  • n - n关系,建立新的关系表,关系表中有两个外键,分别为实体表的主键,并加入额外相应的列。
  • 1 - 1 关系,优化E-R图。

以下为上述 E-R图建的表。


注:红色的标志为主键,*表示联合主键,绿色的表示外键。
这就是一个符合第三范式的,我们最常用的表设计流程了。

拓展阅读 —— 关系表的一种猥琐处理方式

闲话不多说,直接看一个实例:


上述 学生和课程的关系应该是 n - n,但是为什么没有关系表了呢?因为我们观察到,课程只有8个。

那么我们就可以在学生表中增加一列,叫做选课关系,该列的值是用8位的int即可,从右往左我们分别叫第0位 ... 第7位。

第n位上为1,代表该学生选了这个课程。反之,第n位为0,则表示该学生没有选该课程。

参考文献:

[1] [详解第一范式、第二范式、第三范式、BCNF范式] blog.csdn.net/wenco1/arti…

写在后面

中间的部分内容,例如范式的递进,其实对思维也是一个训练,知其然、知其所以然,是两个不同的概念。

文章中如有错误,欢迎指出。