<< ..

数据库的范式

发布时间:

评价数据库中表设计的是否合理可以用到数据库范式的概念。 但是理解数据库范式会让人有点稍微头大。 其实是很基本的关系依赖的概念,但是读到的所有的解释都在人为的复杂化。

实际上理解困难的根本原因是学术概念到实际应用在说辞上不一致。我觉得在搞清楚范式前,首先需要找到概念词和实际应用的对应关系,数据库说白了就是对现实世界的映射,比如你完全可以通过公司的数据库内各个表信息反推出公司的业务场景。 能理解这个点很重要,无论数据库里设计多少表,终究的目的还是反应出实际场景中的信息含义。把握住通过数据库信息理解现实世界信息的理念,再去看这门学科各个术语。

下面,假如我们在经营一家公司(建个库):

  1. 实体,”一个部门“,”一个员工“,这是一行数据,却反应了一个现实世界的概念。
  2. 属性,你在开一家公司,造一张company表,那公司里的部门就是一个属性,company表里有个字段叫department。
  3. 元组,表里的每一行记录,(部门号1,信息服务部)
  4. 码,唯一标记,id
  5. 外码,关联键

在这些基础概念之上,虽然足够创建一张表去反应现实,但是如果不精心设计如何组合各个属性,就会导致产生冗余的数据,无效信息会持续增加。 这个时候就引入了”范式”的概念,用范式来规范建表,合理搭配,从逻辑层面削减无效信息。

1NF

第一范式,保证字段的原子性。对关系的基本要求就是不要出现一个字段下有多个含义的值出现,比如:

学号 姓名 电话
1 狗蛋 狗蛋:123456 狗蛋他妈: 798465
2 豆花 豆花:852652

这张表电话字段的设计就存在歧义性,到底是谁电话,含义不够原子性,正确的设计方式是让字段含义更明确:

学号 学生姓名 学生电话
1 狗蛋 123456
2 豆花 852652

结合第一范式对实际建表的启发就是字段含义的定义除了满足原子性外,命名也真的很重要,虽然电话可以用phonestu_phone字段名声明,但是更适配未来的做法是用stu_phone,未来与不同表不同系统对接时候,也可以保证不会撞了字段含义。

想符合第一范式就来一个含义单一用途明确的字段名。

2NF

所谓2NF,在满足1NF的设计之上,需要让表中出现的每个字段都和表主键关联。举个例子:

订单号 商品号 下单日 价格
001 AAA 20200818 12000
002 BBB 20200521 14000

这张表订单号是唯一id,也就是所谓的”码”。 表面上设计很合理,某人下单昨天花12000买了个锤子。 实际需要更深一层思考,锤子价格和用户什么时候下单没有绝对关系。 从逻辑上完全是两回事情。 价格字段主要关联的是商品号字段,跟订单号字段没有强关联。

2NF的要求是表中的每个字段都要和主键关联。 于是我们需要改造一番,把价格字段拆出去。

订单表中字段都强关联主键:

订单号 商品号 下单日
001 AAA 20200818
002 BBB 20200521

商品价值只存于商品表中,减少了空间占用:

商品号 价格
AAA 12000
BBB 14000

最终把一张表拆成了两张表实现了符合2NF。单行记录的表达能力缩减,通过多表关联才能实现未改造之前的含义组合。

想符合第二范式就来一个含义单一用途明确的表。表如果有关联信息,就通过关联主键方式关联得出。

3NF

继续,在满足2NF之上。已经设计出了用途明确的一张表,表内也有含义明确的字段了。 基于以上认知,进一步优化。

优化的要求是,在表结构中出现的各种字段不能包含其它表中的非主键关键字信息。 对应到表中就是,新设计的表A.a字段不能和表B.b字段的含义重复。 这里的ab如果本来表示的是相同意思,那就应该考虑单独成表。

2NF只保证了单表信息精简,3NF要做到多表之间的信息精简。

总之,设计符合范式的表,本质是在从逻辑搭配上不断抽象和合并,将冗余的信息一点点合并。

反范式

在数据仓库设计中,特别是一些信息聚合层的数仓表,恰恰要做反第三范式的设计。 我觉得根本理由是为了方便取数。 毕竟数仓的需求场景是分析用,要尽可能关联信息得出分析结果。 其次在硬件资源和技术层面,数仓建设一般使用的不是传统的关系型数据库,而是类Hive的数据仓库技术,底层分布式存储撑腰。