范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。反之则是乱七八糟,不仅给数据库的编程人员制造麻烦,而且面目可憎,可能存储了大量不需要的冗余信息。

        设计范式是不是很难懂呢?非也,大学教材上给我们一堆数学公式我们当然看不懂,也记不住。所以我们很多人就根本不按照范式来设计数据库。

        实质上,设计范式用很形象、很简洁的话语就能说清楚,道明白。

        本文将对范式进行通俗地说明,并以笔者曾经设计的一个简单论坛的数据库为例来讲解怎样将这些范式应用于实际工程。

范式说明

        第一范式(1NF):

        数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型实数字符型逻辑型日期型等

        例如:

        如下的数据库表是符合第一范式的:

        字段1 字段2 字段3 字段4

        而这样的数据库表是不符合第一范式的:

        字段1 字段2 字段3 字段4

        字段3.1 字段3.2

        很显然,在当前的任何关系数据库管理系统(DBMS)中,傻瓜也不可能做出不符合第一范式的数据库,因为这些DBMS不允许你把数据库表的一列再分成二列或多列。因此,你想在现有的DBMS中设计出不符合第一范式的数据库都是不可能的。

      第二范式(2NF):

        数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。

        假定选课关系表为SelectCourse(学号, 姓名, 年龄, 课程名称, 成绩, 学分),关键字为组合关键字(学号, 课程名称),因为存在如下决定关系:

        (学号, 课程名称) → (姓名, 年龄, 成绩, 学分)

        这个数据库表不满足第二范式,因为存在如下决定关系:

        (课程名称) → (学分)

        (学号) → (姓名, 年龄)

        即存在组合关键字中的字段决定非关键字的情况。

由于不符合2NF,这个选课关系表会存在如下问题:

        (1) 数据冗余:

        同一门课程由n个学生选修,"学分"就重复n-1次;同一个学生选修了m门课程,姓名和年龄就重复了m-1次。

        (2) 更新异常:

        若调整了某门课程的学分,数据表中所有行的"学分"值都要更新,否则会出现同一门课程学分不同的情况。

        (3) 插入异常:

        假设要开设一门新的课程,暂时还没有人选修。这样,由于还没有"学号"关键字,课程名称和学分也无法记录入数据库。

        (4) 删除异常:

        假设一批学生已经完成课程的选修,这些选修记录就应该从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。很显然,这也会导致插入异常。

        把选课关系表SelectCourse改为如下三个表:

        学生:Student(学号, 姓名, 年龄);

        课程:Course(课程名称, 学分);

        选课关系:SelectCourse(学号, 课程名称, 成绩)。

        这样的数据库表是符合第二范式的, 消除了数据冗余、更新异常、插入异常和删除异常。

        另外,所有单关键字的数据库表都符合第二范式,因为不可能存在组合关键字

      第三范式(3NF):

        在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系:

        关键字段 → 非关键字段x → 非关键字段y

        假定学生关系表为Student(学号, 姓名, 年龄, 所在学院, 学院地点, 学院电话),关键字为单一关键字"学号",因为存在如下决定关系:

        (学号) → (姓名, 年龄, 所在学院, 学院地点, 学院电话)

        这个数据库是符合2NF的,但是不符合3NF,因为存在如下决定关系:

        (学号) → (所在学院) → (学院地点, 学院电话)

        即存在非关键字段"学院地点"、"学院电话"对关键字段"学号"的传递函数依赖

        它也会存在数据冗余、更新异常、插入异常和删除异常的情况,读者可自行分析得知。

        把学生关系表分为如下两个表:

        学生:(学号, 姓名, 年龄, 所在学院);

        学院:(学院, 地点, 电话)。

        这样的数据库表是符合第三范式的,消除了数据冗余、更新异常、插入异常和删除异常。

鲍依斯-科得范式(BCNF):

        在第三范式的基础上,数据库表中如果不存在任何字段任一候选关键字段的传递函数依赖则符合第三范式。

        假设仓库管理关系表为StorehouseManage(仓库ID, 存储物品ID, 管理员ID, 数量),且有一个管理员只在一个仓库工作;一个仓库可以存储多种物品。这个数据库表中存在如下决定关系:

        (仓库ID, 存储物品ID) →(管理员ID, 数量)

        (管理员ID, 存储物品ID) → (仓库ID, 数量)

        所以,(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都是StorehouseManage的候选关键字,表中的唯一非关键字段数量,它是符合第三范式的。但是,由于存在如下决定关系:

        (仓库ID) → (管理员ID)

        (管理员ID) → (仓库ID)

即存在关键字段决定关键字段的情况,所以其不符合BCNF范式。它会出现如下异常情况:

        (1) 删除异常:

        当仓库被清空后,所有"存储物品ID"和"数量"信息被删除的同时,"仓库ID"和"管理员ID"信息也被删除了。

        (2) 插入异常:

        当仓库没有存储任何物品时,无法给仓库分配管理员

        (3) 更新异常:

        如果仓库换了管理员,则表中所有行的管理员ID都要修改

        把仓库管理关系表分解为二个关系表:

        仓库管理:StorehouseManage(仓库ID, 管理员ID);

        仓库:Storehouse(仓库ID, 存储物品ID, 数量)。

        这样的数据库表是符合BCNF范式的,消除了删除异常、插入异常和更新异常。

范式应用

        我们来逐步搞定一个论坛的数据库,有如下信息(有待于研究):

        (1) 用户:用户名,email,主页,电话,联系地址

        (2) 帖子:发帖标题,发帖内容,回复标题,回复内容

        第一次我们将数据库设计为仅仅存在表

        用户名 email 主页 电话 联系地址 发帖标题 发帖内容 回复标题 回复内容

        这个数据库表符合第一范式,但是没有任何一组候选关键字能决定数据库表的整行,唯一的关键字段:用户名也不能完全决定整个元组。我们需要增加"发帖ID"、"回复ID"字段,即将表修改为:

        用户名 email 主页 电话 联系地址 发帖ID 发帖标题 发帖内容 回复ID 回复标题 回复内容

        这样数据表中的关键字(用户名,发帖ID,回复ID)能决定整行:

        (用户名,发帖ID,回复ID) → (email,主页,电话,联系地址,发帖标题,发帖内容,回复标题,回复内容)

        但是,这样的设计不符合第二范式,因为存在如下决定关系:

        (用户名) → (email,主页,电话,联系地址)

        (发帖ID) → (发帖标题,发帖内容)

        (回复ID) → (回复标题,回复内容)

        即非关键字段部分函数依赖于候选关键字段,很明显,这个设计会导致大量的数据冗余和操作异常。

        我们将数据库表分解为(带下划线的为关键字):

        (1) 用户信息:用户名,email,主页,电话,联系地址

        (2) 帖子信息:发帖ID,标题,内容

        (3) 回复信息:回复ID,标题,内容

        (4) 发贴:用户名,发帖ID

        (5) 回复:发帖ID,回复ID

        这样的设计是满足第1、2、3范式和BCNF范式要求的,但是这样的设计是不是最好的呢?

        不一定。

        观察可知,第4项"发帖"中的"用户名"和"发帖ID"之间是1:N的关系,因此我们可以把"发帖"合并到第2项的"帖子信息"中;第5项"回复"中的"发帖ID"和"回复ID"之间也是1:N的关系,因此我们可以把"回复"合并到第3项的"回复信息"中。这样可以一定量地减少数据冗余,新的设计为:

        (1) 用户信息:用户名,email,主页,电话,联系地址

        (2) 帖子信息:用户名,发帖ID,标题,内容

        (3) 回复信息:发帖ID,回复ID,标题,内容

        数据库表1显然满足所有范式的要求;

        数据库表2中存在非关键字段"标题"、"内容"对关键字段"发帖ID"的部分函数依赖,即不满足第二范式的要求,但是这一设计并不会导致数据冗余和操作异常;

        数据库表3中也存在非关键字段"标题"、"内容"对关键字段"回复ID"的部分函数依赖,也不满足第二范式的要求,但是与数据库表2相似,这一设计也不会导致数据冗余和操作异常。

        由此可以看出,并不一定要强行满足范式的要求

        对于1:N关系:

        当1的一边合并到N的那边后,N的那边就不再满足第二范式了,但是这种设计反而比较好!

        对于M:N的关系:

        不能将M一边或N一边合并到另一边去,这样会导致不符合范式要求,同时导致操作异常和数据冗余。

        对于1:1的关系:

        我们可以将左边的1或者右边的1合并到另一边去,设计导致不符合范式要求,但是并不会导致操作异常和数据冗余。

        结论

        满足范式要求的数据库设计是结构清晰的,同时可避免数据冗余和操作异常。这并意味着不符合范式要求的设计一定是错误的,在数据库表中存在1:1或1:N关系这种较特殊的情况下,合并导致的不符合范式要求反而是合理的。

在我们设计数据库的时候,一定要时刻考虑范式的要求。

数据库的第一范式,第二范式,第三范式,BCNF范式理解

        第一范式 属性的原子性

        所谓的第一范式就是数据库中的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性,如果出现重复的属性需要重新构建实体新的实体由重复的属性构成

见图

        分析图: 在进货和销售中 有两个重复的属性(数量和单价),并且进价和销售是可以再分割的,不满足原子性,即不满足第一范式,可以修改为下面的两个实体

        第二范式 属性完全依赖于主键

        第二范式是在第一范式的基础上建立起来的,即满足第二范式必须先满足第一范式,第二范式要求数据库的每个实例或行必须可以被唯一的区分,即表中要有一列属性可以将实体完全区分,这个属性就是主键,即每一个属性完全依赖于主键,在员工管理中,员工可以通过员工编号进行唯一区分,

        完全依赖概念:即非主属性不能依赖于主键的部分属性,必须依赖于主键的所有属性。

        第三范式

        满足第三范式必须先满足第二范式,第三范式要求一个数据库表中不包含已在其他表中已包含的非主关键字信息。

         例如: 存在一个课程表,课程表中有课程号(Cno),课程名(Cname),学分(Ccredit),那么在学生信息表中就没必要再把课程名,学分再存储到学生表中,这样会造成数据的冗余, 第三范式就是属性不依赖与其他非主属性,也就是说,如果存在非主属性对于码的传递函数依赖,则不符合第三范式

        这个例子就是典型的非3NF 两个非主属性

        属性不依赖与其他非主属性,则不符合第三范式

                --------选修课程名---->选修课程号(非主属性)

        如果存在非主属性对于码的传递函数依赖,则不符合第三范式

        理解为

                --------选修课程名---->选修课程号------> 学号(传递依赖)

        不是第三范式

        BCNF 范式

        满足BCNF范式的条件如下:

        所有的非主属性对每一个码都是完全函数依赖 (暗含 主关键字里面可能有多个码可以将实体区分)

        所有的主属性对每一个不包含它的码也是完全函数依赖(即所选码与未选择的码之间也是完全函数依赖的)

        没有任何属性完全函数依赖于非码的任何一组属性(即非主属性之间不能函数依赖)

        解释:

        例如关系模式 S(Sno,Sname,Sdept,Sage) 假设 Sname具有唯一性

        解释条件1:

        非主属性 (Sdept,Sage) 不仅依赖于Sno,而且依赖于Sname,因为不仅可以通过学号知道学生的信息,还可以通过姓名知道学生的信息。

        解释条件2:

        Sno 与Sname之间也是完全函数依赖关系

        解释条件3:

        没有任何一个属性函数依赖于Sdept和Sage

举例说明数据库一、二、三及BCNF范式相关推荐

  1. 三十二、数据库设计的三范式【完】

    数据库设计的三范式 第一范式 数据库表中不能出现重复记录,每个字段是原子性的不能再分 不符合第一范式的示例 学生编号 学生姓名 联系方式 1001 张三 zs@gmail.com,1359999999 ...

  2. MySQL数据库(二)高级

    MySQL数据库(二)高级 一.试图 1.创建试图 视图是一个虚拟表,是sql的查询结果,,本身是不具有数据的,占用很少的内存空间,它是 SQL 中的一个重要概念其内容由查询定义.同真实的表一样,视图 ...

  3. 数据库事务的三种提交方式介绍

    文章目录 数据库事务提交的三种方式: 为什么需要隐式提交: 通过不同的数据库来介绍自动提交和手动提交: 举例: 数据库事务提交的三种方式: 手动显式提交.隐式提交及自动提交. 参考:https://b ...

  4. proc除了能用于oracle开发_能不能用于mysql开发_Oracle数据库开发(二).Linux下配置使用ProC...

    一.提要 上文简单介绍了Windows下ProC配置开发,这次我们使用Linux平台再次配置Oracle ProC开 发环境(RedHat Linux 9 + Oracle 92). <ORAC ...

  5. Oracle数据库备份与恢复的三种方法

    Oracle数据库备份与恢复的三种方法 Oracle数据库有三种标准的备份方法,它们分别是导出/导入(EXP/IMP).热备份和冷备份.导出备件是一种逻辑备份,冷备份和热备份是物理备份. 一. 导出/ ...

  6. psycopg2 mysql_使用psycopg2操作PostgreSQL数据库之二

    使用psycopg2操作PostgreSQL数据库之二 佣工7001 1. 连接数据库: import psycopg2 import psycopg2.extras conn = psycopg2. ...

  7. 使用psycopg2操作PostgreSQL数据库之二

    使用psycopg2操作PostgreSQL数据库之二 佣工7001 1. 连接数据库: import psycopg2import psycopg2.extrasconn = psycopg2.co ...

  8. [Qt教程] 第22篇 数据库(二)编译MySQL数据库驱动

    [Qt教程] 第22篇 数据库(二)编译MySQL数据库驱动 楼主  发表于 2013-5-13 21:28:02 | 查看: 1616| 回复: 12 编译MyQSL数据库驱动 版权声明 该文章原创 ...

  9. Redis数据库(二)——数据类型

    Redis数据库(二)--数据类型 一.String数据类型 1.set / get / append / strlen 2.incr / decr / incrby / decrby 3.getse ...

  10. Redis数据库(二)——Redis高可用、持久化及性能管理

    Redis数据库(二)--Redis高可用.持久化及性能管理 一.Redis 高可用 主要的高可用技术 二.Redis 持久化 1.持久化的功能 2.两种持久化方式 3.RDB 和 AOF 的区别 ① ...

最新文章

  1. 西门子S7以太网通讯协议
  2. 中国电子银行网 神策数据:银行数字营销现状洞察报告
  3. js模板引擎——art Template
  4. ad域时间源配置_域控制器server2012时间同步NTP配置
  5. 【ICLR2019】Poster 论文汇总
  6. Python | 多种编码文件(中文)乱码问题解决
  7. class.forname找不到类_自媒体情感类文章素材怎么找?
  8. Apache提示You don't have permission to access / on this server问题解决
  9. VMware 中的操作系统切换模式后总是连接不上互联网可能的问题之一
  10. 约当标准型_特征向量到约当标准型
  11. 清理linux系统内存缓存
  12. mac打包dmg文件
  13. ASP.NET SignalR 与LayIM配合,轻松实现网站客服聊天室(三) 激动人心的时刻到啦,实现1v1聊天...
  14. Vue中的 Filters 过滤器
  15. Java 计算时间差
  16. zookeeper连接,报caught end of stream exception EndOfStreamException: Unable to read additional data fro
  17. Python中的GIL问题
  18. FunHouse-F10-MPro-1005G1-Hackintosh-Opencore 黑苹果efi引导文件
  19. ubuntu有用的网址
  20. 单反相机快门速度怎么设置

热门文章

  1. linux文件的复制需要什么权限,linux下复制文件需要什么样权限
  2. 群相册上传照片显示服务器繁忙,QQ相册上传速度慢怎么办 QQ相册上传不了照片解决方法...
  3. Nessus插件包20220925
  4. FastAPI获年度第一新兴框架,2021年最受欢迎的TOP 100开发工具出炉
  5. QTcpSocket使用发现的问题
  6. 基于django的视频点播网站开发-step15-项目部署
  7. 计算机软考中级可以考监理工程师吗,软考中级信息系统监理师考试要求
  8. html如何自动获取屏幕高度,css如何获取屏幕高度?
  9. 贴片电容的命名规则和参数解释
  10. 苹果电脑Mac电脑使用心得M1芯片快捷键