• 在数据库设计的时候,如何构建一个合适合理的数据模式,又是用什么标准来评判数据库设计的优劣的呢?————关系数据库的规范化理论

何为关系模式?

关系是一个由五个部分组成的五元组 R(U,D,DOM,F)

  • R:关系名
  • U:属性名集合
  • D:U中属性的域
  • DOM:属性向域的映像集合
  • F: 属性间的依赖关系集合

举个例子的话在学生学科关系中

  • R:学生学科
  • U:(学生id,科目id,成绩)
  • D:(int,int, int)

关系我们在前面学习过,使用的是二维表来表示关系, 在实际的运用中,由于我们自己有关心的重点,所以关系模式也可以简化为 R(U,F)

上面这些我们已经知道了,那么这个属性间的依赖关系集合F是个什么东西??

数据依赖为何物?

数据依赖即为一个关系中属性属性之间的约束关系。 这个约束关系是我们在现实生活中实际使用的时候发现的。

数据依赖主要可分为:

  • 函数依赖(Functional Dependency--FD) 某个属性集决定另一个属性集时,称另一属性集函数依赖于该属性集。
  • 多值依赖(Multivalued Dependency--MVD) 定义较为复杂,暂且不谈。

就拿函数依赖为例子,我们在现实中发现,在学生信息中,学号决定了一个学生的姓名,班级等等,同一个学号不可能对应两个姓名。 那么此时可以说学生的姓名是函数依赖于学生的学号的。

记作:

学生学号--->学生姓名

再举一个例子的话,就是学生的学号和科目决定了成绩属性 记作:

(学生学号,科目编号) ----> 成绩

设计的不好的关系模式会出现什么问题??

设计的不好的关系模式会出现的问题一般有以下几种

数据冗余大

可以看到明明王建同学的系与年龄属性是不会变化的,却多次出现在表中,占据了不少空间。 这就是数据冗余。

修改(更新,删除,插入)异常

  • 在上图中若是想要更新王建同学的年龄,就要修改足足五条数据!! 这不但增加了修改的开销,还使得修改的错误可能变大了,毕竟操作得越多,错的可能越大嘛。
  • 若想修改张艺谋(原来你是cs系的!?)数据,将其删除,那课程C107从此从数据库中消失了,再也找不到这个课程的任何痕迹。
  • 若转校来了一个新同学小黑,他才刚来,没有选课,想要把他的信息插入到这张表里。但我们可以看见光学生的id是不能确定一条数据的,这张表的码是(Sno,Cno),而小黑的Cno是空的,那么可怜的小黑就因为违反了实体完整性约束没有办法加入到数据库中了。

关系设计的老大哥——规范化

正是因为随心所欲的设计会带来一箩筐的问题,业界大佬们纷纷提出完善了规范化理论,通过分解关系模式来消除以上遇到的四个问题。

在提到具体的规范化怎么做的之前,为了沟通无障碍,先引入那么一点点一咪咪一丝丝的概念。

喵了个咪这么多概念!?

函数依赖的正式定义

设 R(U) 是一个属性集 U 上的关系模式,X 和 Y 是 U 的子集。 若对于R(U) 的任意一个可能的关系r ,r 中不可能存在两个元组在X 上的属性值相等, 而在Y 上的属性值不等, 则称 “X 函数确定Y” 或 “Y 函数依赖于X” ,记作X→Y 。


其中X为这个函数依赖的决定属性组,称为决定因素

非平凡函数依赖

若X ---> Y,但Y X,则X ---> Y为非平凡函数依赖

平凡函数依赖

若 X ---> Y,但Y X ,则 X ----> Y为平凡函数依赖

栗子: 非平凡函数依赖: (Sno,Cno)→ Grade

平凡函数依赖: (Sno, Cno) → Sno (Sno, Cno) → Cno

完全函数依赖

若 X ----> Y,且 X 的任意一个真子集 X' 都不会有 X' -----> Y, 则 Y 完全函数依赖与 X

如 (Sno,Cno)→ Grade

部分函数依赖

若 X -----> Y,但 Y 不完全函数依赖与 X, 则 Y 部分函数依赖与 X

如 (Sno, Cno) → Sname

因为明明 Sno → Sname 就可以了

传递函数依赖

若(Y)完全函数依赖于(X), 且 Y X,Y ----> Z, 则称 Z 对 X 传递函数依赖。

如 学生id -> 系id 系id -> 系主任id 则"学生id"传递函数依赖于"系主任id"

候选码

在关系R中,若有Y完全函数依赖于X,则X为R的候选码


在下文中“候选码”可简称为“”。

(但实际的"码"范围更广,只要能确定一条记录的都是码,那么所有属性的集合也是码,但这样说没啥意义,所以下文的"码"指的都是"候选码")

由定义可以知道,候选码可能不止有一个,可以在一堆中选一个作为主码(primary key)。

主属性

包含在任何一个候选码中的属性。

非主属性

没有包含在任何一个候选码中的属性。

全码

整个属性组U都是码,称为全码。

(到这里我是已经“全麻”了)

栗子: 关系模式 R (P ,W ,A ) P :演奏者 W :作品 A :听众 一个演奏者可以演奏多个作品。 某一作品可被多个演奏者演奏。 听众可以欣赏不同演奏者的不同作品。 码为(P ,W ,A) ,即All-Key (但这样的表有什么意义吗...)

范式!

痛苦的概念部分终于结束了!!

来说说范式,范式是符合某一种级别的关系模式的集合,关系数据库中的关系必须满足一定的要求。 满足不同程度要求的为不同范式。

范式的种类:


各个范式之间是有联系的,位于前方的范式是被后面的真包含的。

一个低一级范式的关系模式,是可以通过分解来转化成较为高级范式的关系模式的集合。

这个过程就叫规范化

具体的范式要满足什么要求呢?

1NF

第一范式是啥?

咳咳,让我正式介绍一下。

第一范式!

您是创造一切的主,全知全能的神;

您是一切关系的根源,是开始,也是结束;

是众范式之范式,是数据库关系的支配者!

说白了,第一范式是对关系模式的最起码的要求,不满足第一范式的数据库模式就压根不能称为关系数据库!

第一范式具体定义为

关系模式R 的所有属性都是不可分的基本数据项

比如表中你不能出现一个属性为“姓名班级”,就应该拆成“姓名”和“班级”

2NF

在讲2NF是啥之前,先看看一个关系。

R(Sno,Cno,Sdept,Sloc,Grade)

五个属性分别是学生id,课程id,系,学生住址,课程成绩。 (其中同一个系的同学的住址是一样的。)

显然他是满足第一范式的,但不用我说,你也看得出来这个关系设计的稀烂,前面说的四个问题:数据冗余大,更新异常,插入异常,删除异常可以在这张表里面完美体现。

来分析一下这个关系的函数依赖 (这里以及下文,讨论的都是非平凡的,平凡的不谈!)

  • (Sno,Cno) -> Grade (完全依赖)
  • (Sno,Cno) -> Sdept (部分)
  • (Sno,Cno) -> Sloc (部分)
  • Sno -> Sdept
  • Sno -> Sloc
  • Sdept -> Sloc

码为(Sno,Cno)

主属性有Sno,Cno 非主属性有Sloc,Grade


真是一团浆糊


2NF的定义:

若R∈1NF,且每一个非主属性完全函数依赖于码,则R∈2NF

这下知道了,为了达到第二范式,得处理那两个部分函数依赖的玩意儿。拆!

R(Sno,Sdept,Sloc)
G(Sno,Cno,Grade)

R的函数依赖:

  • Sno -> Sdept
  • Sno -> Sloc
  • Sdept -> Sloc

码:Sno

G的函数依赖:

  • (Sno,Cno) -> Grade (完全依赖)

码: Sno

完美,全是完全依赖了。

3NF

3NF的定义:

若R∈3NF,则每一个非主属性既不部分依赖于码也不传递依赖于码

完成第二范式已经挺不错的了,第三范式又是什么鬼?

不急,看看完成第二范式的关系集合中的R。

R(Sno,Sdept,Sloc)

函数依赖:

  • Sno -> Sdept
  • Sno -> Sloc
  • Sdept -> Sloc

看着这个图,觉得有问题吧?这三者是否还是有点剪不断理还乱的感觉呢?

看看上面提到的四大问题:

  • 系id与与住所是绑定关系,却多次出现在表中,明显冗余
  • 插入一个暂时没有同学的新系,无法给它分配住址,因为学生id是主属性,根据实体完整性的要求,主属性不能为空。
  • 需要删除一个系所有同学的记录(他们不约而同都一起转学了),系与住所的信息也被随之删除了。
  • 如果某系更换了住所,这个系有几条记录,就要更新多少次信息。

之所以出现了这些问题,是因为Sloc传递依赖于Sno。为了让这些问题解决,我们还是可以继续拆分。

R(Sno,Sdept)
G(Sno,Cno,Grade)
D(Sdept,Sloc)

现在的图是不是清爽多了?

BCNF

BCNF的定义:

关系模式R∈1NF, X ---> Y 且 Y X 时 ,X必含有码, 则R ∈BCNF

比起前面几个,BCNF的定义有亿点点复杂。但仔细反复阅读定义的话它其实就是在说:每一个决定属性因素都包含码(除了决定自己的子集,也就是平凡的)。


依然举一个栗子: 对于如下关系:

R(Rno,Eno,Gno,Gnum)

其中四个属性分别为:仓库id,仓库员工id,货物种类id,货物数量。这里一个仓库只有一个员工,一个员工也只负责一个仓库。一个仓库可以有很多种货物。

函数依赖为:

  • Rno -> Eno
  • Eno -> Rno
  • (Rno,Gno) -> Gnum
  • (Eno,Gno) -> Gnum

码为(Rno,Gno),(Eno,Gno)

主属性为:Rno,Eno,Gno 非主属性为:Gnum

可以看见,没有一个非主属性部分依赖于码(第三条函数依赖),没有一个非主属性传递依赖于码。所以这是3NF是没有问题的。

但上面提到的四大问题都解决了吗?

  • 仓库id与与员工id是绑定关系,却多次出现在表中,明显冗余啦。
  • 插入一个空仓库,无法给仓库加上员工,因为物品种类id也是主属性,根据实体完整性的要求,主属性不能为空。
  • 某仓库清空后,需要删除所有与这个仓库相关的物品存放记录,仓库本身与员工的信息也被随之删除了。
  • 如果某仓库更换了员工,这个仓库有几条物品存放记录,就要更新多少次员工信息。

这个时候如果使用了BCNF的定义的话,要求每一个决定属性因素都包含码,那么上面函数依赖的1,2就不符合条件了,它们的决定因素都不包含码。那就拆!!

R(Rno,Gno,Gnum)
R-E(Rno,Eno)

R函数依赖:(码为(Rno,Gno))

  • (Rno,Gno) -> Gnum

R-E的函数依赖:(码为(Rno))

  • Rno -> Eno

可以,清爽!


前面说过每一个前面的范式都被后面的范式真包含,那这如何说明 BCNF 3NF 呢?

3NF需要满足两个条件

  • 每一个非主属性不部分依赖于码
  • 每一个非主属性不传递依赖于码

在BCNF中:

  • 所有非主属性对一个码都是完全依赖。
  • 所有的主属性对每一个不包含它的码,也是完全函数依赖
  • 没有一组属性函数完全依赖于非码,所以不会出现非主属性传递依赖于码。

所以一个关系是BCNF的话,就是3NF的。

其他

虽然后面有第四范式,第五范式,但是在实际用途中用的很少,更多的是科研用途了,所以就不说了。