数仓模型案例
一、范式建模
1.零范式
为便于分级说明三范式的特点,我们将不满足任何范式即无范式的数据称为零范式,假设它只满足一个最基本的条件——数据中不存在重复数据。
假设根据零范式的定义数据库中有一张保险订单统计表,表中包含了用户id、保险id、用户名、注册省份、注册城市、注册区县、保险名称、购买信息(价格、数量)、总保费、购买日期。具体情况如下图:
数仓模型示例_第1张图片
2.一范式
在零范式的基础上加上字段具有原子性即属性不可分这个条件后便形成了符合一范式的表。基于上面的保险订单统计表一范式和零范式的区别主要在于将表中的“购买信息”这个字段进行了拆分,形成了“保险价格”和“购买数量”两个字段,这样的数据表我们称其满足一范式。
数仓模型示例_第2张图片
3.二范式
相对于一范式,二范式的条件更为严苛。如果要满足二范式,则数据表需要确保每一列都和主键完全相关,而不能只与主键的某一部分相关(主要针对联合主键而言,我们这里的保险订单统计表使用的即为联合主键)。
在保险订单统计表中,购买数量、总保费和购买日期属于完全依赖联合主键(用户ID,保险ID),但是保险名称和保险价格只依赖于保险ID,用户信息则只依赖于用户ID,它们都属于对主键部分依赖。显然,这个时候保险订单统计表不满足二范式,下面对其进行调整。
将用户信息和保险信息从原表中拆出来单独建表存放,形成如下三张表:保险订单统计表,保险信息表和用户信息表。
数仓模型示例
4.三范式
三范式需要确保数据表中的每一个字段都直接依赖该表的主键,而不能间接依赖。经过二范式的拆解后,已经将最初的一张表变成了三张表。在这三张表中,我们发现用户信息表中的用户ID和注册地址信息是存在传递依赖的,即:用户ID决定地址ID,地址ID决定省份、城市、区县,这属于传递依赖。
因此,需要将用户信息表地址信息表中的地址单独拎出来才满足三范式。
数仓模型示例
以上即为三范式建模的一个大致流程,从上面的模型可以看出三范式建模可以最大限度避免数据的冗余,同时整个业务看起来也更加清晰,不足点是使用起来需要多表关联,不太方便。这类模型比较适合用于数仓公共层中的比较稳定的业务。
二、维度建模
维度建模主要以分析决策的目的进行模型构建,构建的数据模型为分析需求提供服务,因此它重点解决用户如何更快速完成分析需求,同时还有较好的大规模复杂查询的响应性能。维度建模的核心部分为事实表和维度表。
事实表:用于存储某一主题下的主干信息。比如上面的保险订单统计,各个用户保险的购买情况即为该主题的主要关注内容。
维度表:指对事实表中主题进行分析的角度数据。比如保险维度表表和用户维度。
用维度建模的方式进行设计,有如下三张表:保险订单统计事实表、保险维度表和用户维度表。这种设计,在范式理论上符合二范式,也称为反三范式。
数仓模型示例
使用维度建模形成的模型也称为星型模型,可以将事实表当作是中间最大的一颗星星,维度表围绕在事实表周围。星型模型进行扩展后可以形成雪花模型,两者之间的主要区别在于维度表是否都和事实表直接相连。星型模型是将同一主题的维度信息冗余在了一张维表中。雪花模型如下图(其中购买月份还可以继续拆分为月和年的维表):
数仓模型示例_第3张图片
有冗余的事实表
维度建模中星型模型的设计符合范式建模中的二范式的设计,但是在实际工作中我们经常会在事实表中存放更多的信息,以便使用起来更方便。
如下图,冗余后的事实表的设计在范式建模中就只满足一范式了。
数仓模型示例_第4张图片

三、数据仓库和数据库的侧重点
在大部分的数据仓库设计中,一般是不怎么考虑是否满足第几范式的,特别是互联网场景下的数据建设就更少考虑数据仓库和范式之间的关系,但是这并不妨碍我们去理解它们设计背后的出发点。至少我们可以搞明白为什么数据仓库设计不用过多关注范式。
我们这里聊到的数据库的设计,可以理解是联机事务处理OLTP(On-Line Transaction Processing),主要是基本的、日常的事务处理,例如银行交易。直白点讲,就是各种增删改查,需要对数据进行操作。而数据仓库,我们可以理解为是联机分析处理OLAP(On-Line Analytical Processing),主要是面向日常数据分析,它的数据主要是插入和查询,基本不涉及删除和修改操作。
本文的主人公-范式,主要优化的是增删改的问题,比如数据冗余、更新异常、删除异常等。这些也正是数据库设计比较关注的点。而数据仓库对这方面的关注度则比较少,数据仓库更关注的是使用是否方便,查询效率是否高,因此在设计数据仓库的时候不必太多关注范式的设计,一般一范式或者二范式就已经够用。
另外,数据仓库不同层级的设计也会用到不同的建模方式,比如说接近业务数据的层次,会更倾向使用范式建模,接近数据分析的层次则会更倾向于维度建模。