为什么模式分解能够解决数据冗余问题?

320 阅读5分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

模式分解好难,怎么解释才简单一点呢?

看这样一个学生选课数据库:

学生编号学生姓名课程编号课程名称成绩
001李想815数据库原理99
001李想362计算机网络80
002东风兮815数据库原理88

我们分析以下,这个数据库中,很显然候选键(主键)是学生编号和课程编号。而学生编号唯一,可以决定学生姓名,课程编号唯一,可以决定课程名称。两个候选键:学生编号和课程编号共同来决定成绩。

这个数据库是冗余的,比如说李想这个学生姓名出现了两次,数据库这门课也出现了两次。

当数据库设计时出现了数据冗余,这就不是一个好的关系模式,这样的关系模式会产生一些问题,比如说数据库冗余,浪费了大量的存储空间,像学生选课数据库,如果关系模式设计为所有的属性都在一张表上,那么如果一个学生选了多门课,就会出现多条记录,每条记录都会存储一遍学生的学号和姓名等学生信息,这些信息被冗余存储了。

不只是冗余存储的问题,还可能会出现插入异常和删除异常问题。比如在一个关系实例中,如果删除了一门课所有学生选课的记录,那么这门课的课程编号,课程名字等课程信息就都随之丢失了,相当于在数据库中找不到这门课了,但这门课实际上还是存在的,这样就出现了删除异常问题。

反过来,这门课没有被任何学生选中,那么我就没办法在数据库中插入这门课的任何信息,因为这条想插入的记录没有任何学生属性,而学生学号属性一般都是主键,根据实体完整性原则,某个主键为空时是不能插入的。这样就出现了插入异常问题。

还有更新异常问题,比如我想改一门课程的课程名字,比如数据库原理这门课,对应的课程编号是815,原来课程名字是《数据库原理》,现在我想将这门课名字改为《数据库原理与设计》那么在数据库中,因为数据被冗余存储了,在多条记录中都存储了课程编号和课程名字,如果我只改第一条被扫描到的记录的课程名字,其他的记录都不改,那么肯定数据不一致了。更复杂一点,如果这个数据库我有很多备份,我只改了其中一个备份里的课程名字,其他备份里的都没改,也不行,这就是更新异常。

那么数据冗余存储会出现这么多问题,是为什么呢?

这是因为关系模式的会有很多属性,这些属性之间存在依赖关系,这是数据库背后的语义来决定的。比如学生选课数据库中,学生姓名和学生编号这两个属性之间,学生编号决定了学生姓名,也就是说学生姓名依赖于学生编号。课程姓名和课程编号这两个属性之间也存在这样的依赖关系。而成绩属性是由学生学号和课程编号属性共同决定的,也存在依赖关系。

插入异常就是因为学生相关的属性和课程相关的属性之间没有依赖关系,比如我只想插入一个学生信息,他还没选修任何课程,那么这条记录就插入不进去,怎么办?那就把没有依赖的拆成两个独立的数据库,学生信息单独存一个数据库,课程信息也单独存一个数据库。这就叫模式分解。

第一个表:

学生编号学生姓名
001李想
002东风兮

第二个表:

课程编号课程名称
815数据库原理
362计算机网络

通过模式分解,将没有依赖关系的两类属性分开。如上两个表,每个表内还是存在依赖关系的,但两个表之间没有依赖关系,这样的好处一是解决了数据冗余的问题,我们看到每个学生的信息只用在第一个表中存储一次就够了;二是解决了插入异常和删除异常问题,因为两个表之间不存在依赖关系,我们插入一个学生信息(003,机器猫)时直接插入第一个表中即可,这个表只有学生编号是候选键,是必选线,这样即使机器猫还没有选修任何课程,也是可以记录入数据库的。

第一个表:

学生编号学生姓名
001李想
002东风兮

第二个表:

课程编号课程名称
815数据库原理
362计算机网络

那更新异常也就解决了,我想改学生姓名,只需要在学生数据库里改一条学生记录就好了,因为拆开后也不冗余存储了。