数据库范式

  • 参考
  • 数据库规范化/范式所解决的问题
    • 数据冗余(信息重复,会造成储存空间的浪费及一些其他问题)
    • 插入异常(如果有限制,导致插入数据时,必须没必要的列也要有值,这种列甚至此时就是没有数据)
    • 删除异常(在某些情况下,当删除一行时,可能会丢失有用的信息,因为这些相对独立的信息没有单独存储到一个表中)
    • 更新异常(冗余信息不仅浪费空间,还会增加更新的难度,想要修改一个信息,结果由于数据冗余,导致需要更新多条记录)
  • 基本概念
    • 首先说明 键字=码字,所以 主键=主码,候选键=候选码...此外也有叫做主关键字,候选关键字的也是一个意思。
    • 主键/码/候选键/候选码
      • 小技巧:假如A是码,那么所有包含了A的属性组,如(A,B)、(A,C)、(A,B,C)等等,都不是码了(因为作为码的要求里有一个“完全函数依赖”)
    • 主属性
    • 非主属性:包含在任何一个码中的属性成为主属性。除了主属性以外的就是非主属性。
    • 超键
      • 能够唯一标识一条记录的属性或属性集。
    • 全码
      • All-key关系模型的所有属性组组成该 关系模式的 候选码,称为全码。即所有属性当作一个码。若关系中只有一个候选码,且这个候选码中包含全部属性,则该候选码为全码。
    • 函数依赖
      • 部分依赖
      • 传递依赖
  • 范式
    • 最常用的是第一、第二、第三范式。BCNF范式有时要考虑?
    • 第一范式
      • 符合1NF的关系中的每个属性都不可再分。
      • 一般成功在数据库中建的表都是符合的1NF的。
      • 目标是确保每列的原子性.如果每列都是不可再分的最小数据单元,则满足第一范式.
      • 例子
        • 每一列属性都是不可再分的属性值,确保每一列的原子性
          • 比如有一个“地址”列,如果需求需要详细得根据省、市等进行过滤,那么就必须把“地址”列细化成“省”、“市”等列,否则就要在代码中对一个字段的数据再次进行拆分,就可能会影响性能。
          • 但实际上,项目中经常会把json数据放在一个字段中,主要是因为json格式、字段不固定,而且太多。
        • 两列的属性相近或相似或一样,尽量合并属性一样的列,确保不产生冗余数据
          • 比如物品表要保存“物品名”、“物品数量”、“物品单价”三个信息,如果建一堆字段如“物品名1”、“物品数量1”、“物品单价1”、“物品名2”、“物品数量2”、“物品单价2”......那么就太冗余了,这些字段太相似,可以统一用“物品名”、“物品数量”、“物品单价”三个字段就够了,表结构不但不能满足足够多物品的要求,还会在物品少时产生冗余。
          • 但实际上,比如之前在途牛的openfire聊天室功能,可能就需要很多这种字段,只是字段类型可能不同,如string1、string2、string3、int1、int2、int3...这种做法仅限于在一张表中用一个type字段区分场景,每个场景的不同字段有不同含义的情况,避免了建很多类似的表。
    • 第二范式与部分函数依赖
      • 消除了部分函数依赖
      • 只有当函数依赖的决定方是组合属性时,讨论部分函数依赖才有意义,当函数依赖的决定方是单属性时,只能是完全函数依赖。表中放的肯定是相关的数据,即至少和一个候选码有关系(间接关系也算,只不过就不满足第三范式了),否则为什么要存到这张表中?
      • 如果不消除,那么就会有属性依赖于部分主属性,主属性不重复,但部分主属性会有重复记录,就会导致该属性也会重复冗余,事实上知道了该部分主属性,就已经决定了该属性,不必在本表中重复出现
      • 即把公共、重复冗余部分提取了出来
      • 第二范式在第一范式的基础上更进一层,其目标是确保表中的每列都和主键(可能是组合的)相关,也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中.如果一个关系满足第一范式,并且除了主键以外的其他列都依赖与该主键.则满足第二范式.
      • 每一行的数据只能与其中一列(应该是说与主属性组?)相关,即一行数据只做一件事。只要数据列中出现数据重复,就要把表拆分开来。不掺杂复杂的关系逻辑。同时对表数据的更新维护也更易操作。
        • 比如订单表的主键是订单号,如果既包含人的id、房间的id,同时又包含了人的详细信息字段,那么一个人如果定了多个房间,就会产生人的信息的冗余。根本原因是这个表相当于一个有许多冗余的细节字段的关系表,导致了数据重复。如果只是拆除一个关系表就还好,这个表字段少,细节字段在两个实体表中
        • 根本体现是这些人的详细字段只由人的id决定
        • 所以在实际项目中,实体的详细字段最好一起放在独立的实体表中,即拆成一个干净的实体表,不要放到关系表中,有点类似于面向对象设计思想
        • 表和表的关系可以通过外键或者关系表的方式实现
    • 第三范式
      • 消除了非主属性对于码的传递函数依赖
      • 表中的所有数据元素不但要能惟一地被主关键字所标识,而且它们之间还必须相互独立,不存在其他的函数关系。
      • 第三范式在第二范式的基础上更进一层,第三范式的目标是确保每列都和主键列直接相关,而不是间接相关.如果一个关系满足第二范式,并且除了主键以外的其他列都这能依赖于主键列,列和列之间不存在相互依赖关系,则满足第三范式.
      • 数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系。像:a-->b-->c 属性之间含有这样的关系,是不符合第三范式的
        • 比如Student表(学号,姓名,年龄,性别,所在院校,院校地址,院校电话)这样一个表结构,就存在上述关系。:学号--> 所在院校 --> (院校地址,院校电话)。这样的表结构,我们应该拆开来,如(学号,姓名,年龄,性别,所在院校)--(所在院校,院校地址,院校电话)
        • 其实目的和效果都和第二范式类似,只是更绝对,表拆的更加独立了,连传递依赖于主键都不行,也觉得不够独立
    • BCNF范式
      • 消除主属性对于码的部分函数依赖与传递函数依赖。
      • 即包含多列的主属性内部各列之间,就存在部分函数依赖与传递函数依赖问题,前面第二和第三范式只是消除了非主属性的这两个问题
  • 规范化的权衡
    • 最终的方案其实都是合理的拆表(也就是“模式分解”)至适当程度,避免难以维护和影响性能
    • 为了满足三大范式,我们的数据操作性能会受到相应的影响,所以,在实际的数据库设计中,既要考虑三大范式,避免数据的冗余和各种数据操作异常;有要考虑到数据访问性能,有时,为了减少表间连接,提高数据库的访问性能,允许适当的数据冗余列,这可能是最合适的数据库设计方案.
    • 三大范式只是一般设计数据库的基本理念,可以建立冗余较小、结构合理的数据库。如果有特殊情况,当然要特殊对待,数据库设计最重要的是看需求跟性能,需求>性能>表结构。所以不能一味的去追求范式建立数据库。
原文地址:https://www.cnblogs.com/wyp1988/p/10869661.html