数据库规范化,数据库范式,和规范化实例 - Go语言中文社区

数据库规范化,数据库范式,和规范化实例


什么是数据库规范化

维基百科的定义如下:

数据库规范化,又称数据库或资料库的正规化、标准化,是数据库设计中的一系列原理和技术,以减少数据库中数据冗余,增进数据的一致性

数据库范式是埃德加·科德设计出来的。在1970年代初,他定义了第一范式(First normal form)第二范式(Second normal form),和**第三范式(Third normal form)**的概念。数据库范式可以理解成一系列的规范或者规则,为了实现数据库规范化的目标,我们需要按照这些范式的规则来优化数据库。

关于这些范式的规则我会在文章后续部分说明。

现在数据库设计最多满足第三范式,普遍认为范式过高,虽然具有对数据关系更好的约束性,但也导致数据关系表增加而令数据库IO更易繁忙,原来交由数据库处理的关系约束现更多在数据库使用程序中完成。

因此,本文数据库的优化实例只做到第三范式。

术语

为了更准确,简洁地定义各个范式的规则,我们需要知道一些术语的含义。对于有些术语来说,我不打算写出它标准化的定义,这是因为标准化定义十分晦涩。对于这样的术语,俺直接给出相应的例子,理解它代表的是什么东西就行。

下面俺给出一个关于上面部分术语的例子。这个例子来源于 yangyang17

假如有以下学生和教师两个表:

Student(student_no, student_name, student_age, student_sex, student_credit, teacher_no)

Teacher(teacher_no, teacher_name, teacher_salary)

超键:Student表中可根据学生编号(student_no),或身份证号(student_credit),或(学生编号,姓名)(student_no,student_name),或(学生编号,身份证号)(student_no,student_credit)等来唯一确定是哪一个学生,因此这些组合都可以作为此表的超键

候选键:候选键属于超键,且是最小的超键,即如果去掉超键组合中任意一个属性就不再是超键了。Student表中候选键为学生编号(student_no),身份证号(student_credit)

主键:主键是候选键中的一个,可人为决定,通常会选择编号来作为表的主键。

数据库的3个范式介绍

在这个小节,俺会用具体的例子来说明每个范式所包含的规则。

第一范式

第一范式的定义:

First normal form (1NF) is a property of a relation in a relational database. A relation is in first normal form if and only if the domain of each attribute contains only atomic (indivisible) values, and the value of each attribute contains only a single value from that domain.

第一范式必须符合下面3个标准:

  1. Eliminate repeating groups in individual tables
  2. Create a separate table for each set of related data
  3. Identify each set of related data with a primary key

下表是一个不符合第一范式的 例子Telephone Number 列并不是原子的,它可以继续拆分下去。

第一范式

解决上面问题的一个直观方法是多引入一列,如下图所示。但是,这种方法仍然有3个问题。第一、下表仍然包含 “repeating group”,即包含概念上相似的属性,电话号。第二、这样做会引入没有意义的顺序。比如,为什么 555-861-2025 放到 Telephone Number1 列,而不是放到 Telephone Number2 列呢?第三、当一个客户再多一个号码时,会修改表结构,增加一列。

第一范式

下面的表结构可以解决上面的问题。由于第一范式要求能唯一地标识出一个元组,而现在 Customer ID 重复了,所以我们需要 Telephone Number 与 Customer ID 的组合才能标识出一个元组。

第一范式

另一种设计方案如下所示,它是使用两张表。

第一范式

第二范式

第二范式必须满足下面2个条件:

  1. be in first normal form (1NF)
  2. not have any non-prime attribute that is dependent on any proper subset of any candidate key of the relation

第2个条件翻译过来的含义为:任何一个非主属性不能依赖于候选键属性集合的真子集属性。

下表的每个值都是单一值,所以它符合第一正规化。但它不符合第二范式。这是因为,一个元件 ID 和供应商 ID 合在一起组成一个主键,但供应商的名称和住址却只和供应商 ID 有关(部分依赖)。

第二范式

把上表的结构改成下面的结构,就符合第二范式了。

第二范式

第三范式

第三范式必须满足下面2个条件:

  1. 符合第二范式
  2. all the attributes in a table are determined only by the candidate keys of that relation and not by any non-prime attributes

也就是说,符合第三范式的表结构不应该包含 transitive dependency. 即,非主属性之间不能有依赖关系。比如下表的结构就包含 transitive dependency,因为非主属性 Author 决定了另一个非主属性 Author Nationality.

第三范式

根据数据库范式优化数据库结构

在这个小节中,俺会给出一个数据库表的初始结构,它不符合数据库范式。我会一步一步地改进它,直到让它满足数据库的第三范式。下表是一个不符合范式的初始结构:

数据库范式

满足第一范式

初始表中的 Subject 列并不是原子的,一种方式是将其拆分成3个列。如下所示:

第一范式

如果用上面的方式,当某本书增加一个 subject 时,就需要更改表结构,增加一列。因此,用下面的方式修改表,使其符合第一范式会更好一些。

第二范式

拆分成上面的结构以后,我们需要把 Subject 表与 Book 表连接起来。由于一本书可能对应多个 subject,而一个 subject 也可能对就多本书,这是一个典型的 多对多 关系。因此我们需要新建一张关联表:

第二范式

满足第二范式

从下表可以看出,{Book} 和 {Book Type} 是一个候选键。但是,只有一个 price 属性是由这2个主属性共同影响的(这是合理的,因为同样一本书,电子版与精装的价格肯定是不同的)。其它的属性都是只由 Book 这个主属性影响的。因此,这不符合第二范式。

第二范式

按照下面的方式修改以后,就符合第二范式了。

第二范式

满足第三范式

由于下表中的非主属性 Genre ID 决定了另一个非主属性 Genre Name,即存在 transitive dependency,所以它并不符合第三范式。

第三范式

改成下面的结构以后,就符合第三范式了。至此,我们从一个不符合任何范式的初始表结构,一步一步地改成了现在这个结构。
第三范式

参考链接

数据库规范化

Database normalization

第二范式

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/xlinsist/article/details/87983035
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-12-11 19:01:15
  • 阅读 ( 1846 )
  • 分类:数据库

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢