维度建模学习
维度建模
版权声明:本文为CSDN博主「Cincinnati_De」的原创文章,
原文链接:https://blog.csdn.net/Cincinnati_De/article/details/120971510
维度建模两大派系 Bill Inmon(数据仓库之父) 的企业信息化工厂模式和 Ralph Kimball (商业智能之父)的维度建模模式
一、维度建模基本概念
1. 1度量和环境
维度建模支持对业务过程的分析,这是通过对业务过程度量进行建模来实现。
什么是度量?实际上通过业务方、需求方交谈,或者阅读报表、图表等,可以很容易地识别度量。
考虑如下业务需求:
店铺上个月的销售额如何?
店铺库存趋势如何?
店铺的访问情况如何(pv,uv)?
店铺访问的熟客占比多少?
这里的销售额、库存、访问量、熟客量就是度量
缺乏上下文和环境来谈论度量,是没有意义的。比如销售额是3000元,并不知道一天还是多天,一件商品还是整个店铺,一个用户单个订单还是多个用户的订单合计?没有上下文和环境,度量没有意义。
所有的维度建模也正是通过对度量和及其上下文和环境的详细设计来实现的。
1.2事实和维度
在Kimball的维度建模理论中,度量称为事实,上下文和环境则称为维度。
通常来说,事实表以数值形式出现,而且一般都被大量文本形式的上下文包围着。这些文本形式的上下文描述了事实的 " 5个W " (When 、where、What、Who、Why)信息,通常可被直观地分割为独立的逻辑块,每一个独立的逻辑块即为一个维度,
比如一个订单可以非常直观地分为商品、卖家、买家等多个维度。
在维度建模和设计过程中,可以根据需求描述或者基于现有报表,很容易地将信息和分析需求分类到事实和度量中。比如业务人员需求为"按照一级类目,统计本店铺上月的销售额情况","按照一级类目"这个描述,很清楚地说明需求方希望对一级类目的销售额进行统计分析,这里的一级类目即为一个维度。类似的是,"上月"为另一个维度,而销售额明显是事实。
1.3事实表
事实表是维度模型中的基本表,或者说核心表。事实上,业务过程的所有度量在维度建模中都是存储在事实表的,除此之外,事实表还存储了引用的维度。
事实表通常和一个企业的业务过程紧密相关,由于一个业务的业务过程数据构成了其所有数据的绝大部分,因此事实表也通常占用了数据仓库存储的绝大部分。比如对于某个超市来说,其销售的明细数据通常占其拥有数据的绝大部分且每天还在不断地累计和增长,而商店、门店、员工、设备等其他数据相对来说稳定且变化不大。
事实表的一行对应一个度量事件。事实上,每行对应的度量事件可粗可细,比如对某个超市来说,在设计其维度模型时,表示顾客购买事件的事实表的一行即可以记录一张顾客的小票,也可以记录顾客小票的一个子项,那么应该到哪种级别?维度建模认为事实表应该包含最底层、最原子的细节,这样会带来最大的灵活性。维度建模中,细节的级别称为事实表的粒度,比如上文顾客购买行为实施表的粒度应该是小票子项,而非小票。
事实表中最常用的度量一般是数值型和可加类型的,比如小票子项的销售数量、销售金额等。可加性对于数据分析来说至关重要,因为数据应用一般不仅检索事实表的单行数据,而往往一次性检索数百、数千乃至数百万行的事实,并且处理这么多行的最有用的和常见的事会是将它们加起来,而且是从各个角度和维度加起来。
但事实表中的度量并不都是可加的,有些是半可加性质的,另一些则是非可加性质的。半加性事实是指仅仅某些维度可加,例如库存,可以把各个地方仓库的库存加起来,或者把一个仓库不同的商品加起来,但是很明显不能把一个仓库同一商品在不同时期的库存加起来。银行的账户余额,但是不能把不同月份的账户余额加起来,非可加性事实则指根本就不能相加的事实,比如商品的价格以及订单的状态等。
除了存储的事实外,事实表都会包含多个相关的外键,用于关联和连接相应的维度表。例如,订单事实表会包含连接到商品表的商品外键、连接到会员表的买家外键、或者连接到门店表的门店外键等。正是通过这些外键,才能进行各个角度、各个维度的分析。
事实表根据粒度的角度划分不同,可分为事务事实表、周期快照事实表和累计快照事实表。事务事实表用于承载事务数据,通常粒度比较低,例如产品交易事务事实、ATM交易事务事实。周期快照事实表用于记录有规律的、固定时间间隔的业务累计数据,通常粒度比较大,例如账户月平均余额事实表。累计快照事实表用于记录具有时间跨度的业务处理过程的整个信息,通常这类事实表比较少见。
这里需要注意的是,在进行事实表的设计时,一定要注意一个事实表只能有一个粒度,不能将不用粒度的事实建立在同一张事实表中。
1.4维度表
维度表是维度建模的灵魂,通常来说,维度表设计得好坏直接决定了而维度建模的好坏。
维度表包含了事实表所记录的业务过程度量的上下文和环境,它们除了记录 "5个W" 等信息外,通常还包含了很多的描述字段和标签字段等。
维度表通常有多列或者多个属性。实际应用中,包含几十甚至上百属性的维度表并不少见。维度表应该尽可能多地包括一些有意义的文字性描述,以方便下游用户使用。
维度属性是查询约束条件(SQL where 条件)、分组(SQL group 语句)与报表标签生成的基本来源。在查询与报表需求中,属性用by 这个单词进行标识。
例如:一个用户表示要按"产品合约编号"与"机构编号"来查看账户余额,那么"产品合约编号"与"机构编号"就必须是可用的维度属性。
维度属性在数仓中承担着一个重要的角色,由于它们实际上是所有令人感兴趣的约束条件与报表标签的来源,是数仓易学易用的关键。在许多方面,数仓不过是纬度属性的体验而已。数仓的能力直接与维度属性的质量和深度成正比。在提供详细的业务用于属性方面所花的时间越多,数据仓库就越好;在保证属性列值的质量方面所花的时间越多,数据仓库就越好。
维度表是进入事实表的入口。丰富的维度属性给出了丰富的分析切割能力。维度给用户提供了共用数仓的接口。最好的属性是文本和离散的,属性应该是真正的文字而不应是一些编码简写符号。应该通过用更为详细的文本属性取代编码,力求最大限度地减少编码在维度表中的使用。有时候在设计数据库时,并不能很确定从数据源析取出的一个数字型数据字段到底应该作为事实还是维度属性看待。通常可以这样来做出决定,即看字段是一个含有许多取值并参与运算的度量值(当事实看待),还是一个变化不多并作为约束条件的离散取值的描述(当维度属性看待)。
1.5星型架构和雪花架构
在理解了事实表和维度表之后,接下来的问题就是如何组合它们。在维度建模中,存在两种组合维度表和事实表的基本架构:星形架构和雪花架构。
当所有维度表直接连接到事实表时,整个组合的形状类似于星星,所以被称为星形架构。星形架构是一种非规范化的结构,其数据存储存在冗余,比如考虑商品的维度表,其品牌信息在商品的每一行中都存在,包括其品牌ID、名称、品牌拥有者等。
通常很多商品的品牌都是一样的,所以在商品维度表中品牌的信息被重复存储了很多次,也就是存在冗余。
当有一个或者多个维度表没有直接连接到事实表,而是通过其他维度表连接到事实表上时,整个组合的形状就像雪花一样,这种架构被称为雪花架构。
雪花架构是对星形架构维度表的规范化,比如上述的商品表例子,在雪花架构中,其每一行仅存储品牌ID,而品牌的所有其他信息(包括品牌名称、拥有者、注册地等所有描述信息)都存储在单独的品牌维度表内。通过品牌ID 这个外键,商品表可以间接获取到所有品牌描述信息。
雪花架构去除了数据冗余,节省了部分存储,但是也给下游用户的使用带来了不便,如下游用户需要分析品牌的销售额,必须自己先用订单表关联商品表,然后用商品表再关联品牌表。正是由于这一点,在维度建模的实际中,雪花架构很少得到使用。
有时候简单的方案是最美的、最有力的,也是最有效的。基于星形架构的维度建模就是这种情况。星形架构牺牲了部分存储的冗余,但是带来了使用上的极度便捷,也使下游用户的使用和学习成本变得非常低。即使是没有任何技术背景或者维度建模背景知识的业务人员,也很容易理解,更何况目前的存储成本极低,多出的这份存储开销相比后续每次的关联计算、用户使用和学习成本来说,是非常划算的。
星形架构中,每个维度都是均等的,所有维度表都是进入事实表的对等入口,用户可以从任一维度、任一维度属性或者任意多个维度组合、任意多个维度属性组合,方便地对数据进行过滤和聚合(汇总、均值、最大、最小等)操作,而且非常符合业务分析直觉。业务是多变的,模型的设计必须能够经受住业务多变的需求。在实际中,可以通过添加新维度或者向维度表中加入维度属性来满足业务新视角的分析需求。
大多数情况下,数据仓库模型设计中都会采用星形架构,但是在某些特殊情况下,比如必须使用桥接表的情况下等,必须使用雪花架构。
二、维度建模的一般过程
维度建模一般采用具有顺序的4个步骤来进行设计,即选择业务过程、定义粒度、确定维度和确定事实。维度建模的这4个步骤贯穿了维度建模的整个过程和环节,下面逐一介绍。
2.1选取业务过程
业务过程即企业和组织的业务活动,他们一般都有相应的源头业务系统支持。对于一个超市来说,其最基本的业务活动就是用户收银台付款;对于一个保险公司来说,最基本的业务活动是理赔和保单等。当然在实际操作中,业务活动有可能并不是那么简单直接,此时听取用户的意见通常是这一环节最为高效的方式。
但需要注意的是,这里谈到的业务过程并不是指业务部门。模型设计中,应将注意力几种放在业务过程而不是业务部门。如果建立的维度模型是同部门捆绑在一起的,就无法避免出现数据不一致的情况(如业务编码、含义等)。因此,确保数据一致性的最佳办法是从企业和公司全局与整体角度,对于某一个业务过程建立单一的、一致的维度模型。
2.2定义粒度
定义粒度意味着对事实表行实际代表的内容和含义给出明确的说明。粒度传递了事实表度量值相联系的细节所达到的程度信息。其实质就是如何描述事实表的单个行。
典型的粒度定义包括:超市顾客小票的每一个子项;
医院收费单的明细子项;
个人银行账户的每一次存款或者取款行为;
个人银行账户每个月的余额快照。
对于维度设计来说,在事实表粒度上达成一致非常重要,如果没有明确的粒度定义,则不能进入后面的环节。如果在后面的环节环节中发现粒度的定义不够或者是错误的,那么也必须返回这一环节重新定义粒度。
在定义粒度过程中,应该最大限度地选择业务过程中最为原子性的粒度,这样可以带来后续的最大灵活度,也可以满足业务用户的任何粒度的分析需求。
2.3确定维度
定义了粒度之后,相关业务过程的细节也就确定了,对应的维度也很容易确定。正如前文所述,维度是对度量的上下文和环境的描述。通过维度、业务过程度量与事实就会变得丰富和丰满起来。对于订单来说,常见的维度会包含商品、日期、卖家、卖家门店等。而每个维度还可以包含大量的描述信息,比如商品维度表会包含商品名称、标签价、商品品牌、商品类目、商品上线时间等。
2.4确定事实
确定事实通过业务过程分析可能要分析什么来确定。定义粒度之后,事实和度量一般也很容易确定,比如超市的订单活动,相关的度量显然是销售数量和销售金额。
在实际维度事实设计中,可能还会碰到度量拆分的问题,比如超市开展单个小票满100减10元的活动,如果小票金额超过10元,这10元的优惠如何分配到每一个小票子项?实际设计中,可以和业务方具体讨论并制定具体的拆分分配算法。
三、维度表设计
维度表是维度建模的灵魂,在维度设计中碰到的问题(如维度变化、维度层次、维度一致性、维度整合和拆分等)直接关系到维度建模的好坏,因此本节将深入介绍维度表设计,并详细解释相关概念和技术。
3.1维度变化
维度表的数据通常来自于前台业务系统,比如商品维度表可能来自于EPR或者超市POS系统的商品表,但商品是会发生变化的,比如商品所属的类目,商品标签价格、商品描述等。这些变化有可能是之前有错误需要订正所致的,或者是实际的业务情况变化。不管哪种情况,维度设计过程中,确定源头数据变化在维度表中如何表示非常重要。在维度建模中,这一现象称为缓慢变化的维度,简称缓慢变化维。
根据变化内容的不同,下游的分析可能要求用不同的办法来处理。比如对于商品的描述信息,也许业务人员对此并不敏感,或者认为无关紧要,此种情况可以直接覆盖。但是对于商品所属的类目发生变化,则需要认真考虑,因为这涉及归类这个商品的销售活动到哪个类目---是全部归到新类目,还是全部归到旧类目?变化前归到旧类目,还是变化后归到新类目?这实际上也涉及了缓慢变化维的几种处理办法。
3.1.1重写维度值
当一个维度值属性发生变化时,重写维度值方法直接用新值覆盖旧值。该技术适用于维度建模中不需要保留此维度属性历史变化的情况,常用于错误订正或者维度属性改变无关紧要的场景,比如用户的生日之前发声输入错误,不需要保留之前的生日历史数据。
采用重写维度值的方法,将改变此维度属性的所有历史度量。比如,分析师希望分析星座和销售的关系,之前用户的生日属于白羊座。因此维度设计人员只在必要情况下使用此方法。同时需要告知下游分析用户。
3.1.2插入新的维度行
相比重写维度值方法不维护维度属性变化的特点,插入新的维度行方法则通过在维度表中插入新的行来保存和记录变化的情况。属性改变前的事实表行和旧的维度值关联,而新的事实表行和新的维度值关联。
仔细观察变化后的维度表可以发现,新复制了一行该用户的信息,唯一不同在于 state 的不同(之前是AZ,之后是CA)。同时,仔细观察订单事实表也会发现,过去的订单是和旧的维度行关联,而新的订单和新的维度行关联。
通过新增维度行,保存了维度的变化,并实现了维度值变化前的事实和变化后的事实分别与各自的新旧维度值关联。但是这也给维度表用户带来了困惑,为什么查询一个会员会在维度表中发现多行记录?尽管可以向用户解释,但是用户的使用和学习成本无疑增加了,而且数据开发人员对于维度变化的处理逻辑无疑更复杂了。
3.1.3插入新的维度列
在某些情况下,可能用户会希望既能用变化前的属性值,又能用变化后的属性值来分析变化前后的所有事实,此时可以采用插入新的维度列这种方法。
不同于前一种方法的添加一行,这种方法通过新增一列,比如用 region_previous 列表示之前的所属大区,同时新增 region_current 来表示变化后的所属大区。
上述示例只能捕获了一次属性值的变化,如果有多次变化呢?此时就需要有多个列来存储。如果不希望新增列,那么将只保存最近的两次变化。
实际上,这三种方法都能从不同角度解决维度变化的问题,还有通过组合这三种方法形成的其他各种技术可用于处理维度变化,在此不一一阐述。
不管哪种技术,在大数据时代都不是完美的,而且有一定的处理复杂度和学习使用成本。如何以一种最简单、直接的办法来解决维度变化呢?后续章节介绍快照技术,以解决大数据时代的维度变化问题。
3.2维度层次
维度层次指的是某个维度表中属性之间存在的从属关系问题。比如商品的类目可能是有层次的(一级类目、二级类目、三级类目等,尤其对于宝洁、联合利华等大的快消企业集团),同时类目、品牌和产品实际上也是有层次的,比如宝洁的化妆品会分为男士、女士和儿童等,而男士化妆品下面又有不同的子品牌,每个子品牌下面又有不同的产品。那么维度建模如何处理这些层次结构呢?
实际上有两种处理办法: 第一种是将所有维度层次结构全部扁平化、冗余存储到一个维度表中,比如商品的一至三级类目分别用三个字段来存储,品牌等的处理也是类似的。第二种是新建类目维度表,并在维度表中维护父子关系。第一种就是前文所说的星型架构,而第二种就是前文所说的雪花架构。在维度建模中,我们采用第一种来处理维度的层级问题,这样反规范化的处理牺牲了部分存储,但是给用户使用带来了便捷,也降低了学习使用成本。
维度层次结构通常和钻取联系在一起,所谓钻取即使对信息的持续深入挖掘。钻取分为向上钻取和向下钻取。比如对于某零售商的年度销售报表,其年度销售总额显示增长20%,那么从时间上分析师哪个季度的增长量比较高呢?此时可以向下分析各个季度的增长率,同样可以继续向下分析到月增值率乃至天增长率,同样的分析也可以应用到类目、品牌等,来分析到底是哪个类目的增长或者哪个品牌的增长导致了年度总销售额的增长 20%。
上述的钻取一般被称为向下钻取,与之相对的是向上钻取。钻取的实质是增加或者减少维度,增加维度(向下钻取)从汇总数据深入到细节数据,而减少维度(向上钻取)则从细节数据概括到汇总数据。通过钻取,用户对数据能更深入地了解数据,更容易发现问题,从而做出正确的决策。
3.3维度一致性
在 Kimball 的维度设计理论中,并没有物理上的数据仓库。数据仓库是在对多个主题、多个业务过程的多次迭代过程中逐步建立的,这些多个主题、多个业务过程的多次迭代过程被从逻辑上划分为数据集市。所谓数据集市一般由一张和多张紧密关联的事实表以及多个维度表组成,一般是部门级的或者面向某个特定的主题。数据仓库则是企业级的、面向主题的、集成的数据集合。
物理上的数据集市组合成逻辑上的数据仓库,但数据集市的建立是逐步完成的,如果分布建立数据集市的过程中维度表不一致,那么数据集市就会变成孤立的集市,不能从逻辑上组成一个集成的数据仓库,而维度一致性的正是为了解决这个问题。
维度一致性的意思是指,两个维度如果有关系,要么就是完全一样的,要门就是一个维度在数学意义上是另一个维度的子集。不一致既包含维度表内容的不一致,也包含维度属性上的不一致,比如对于一个电子商务公司,如果其浏览等相关主题域的商品维度表包含了该企业的所有商品的访问信息,但是由于某种原因其交易域的商品缺失了部分商品(有可能是成交在其他平台完成),那么对这些缺失商品的交易分析就无法完成。同样如果两者的商品属性不同,比如日期格式、类目划分(有可能浏览分为前天类目,成交是后台类目)等不一致,那么浏览域和交易域的对类目和日期的交叉分析就无法进行,因为其类目划分就不一致。
比如希望分析某类目的用户浏览-成交转化率(计算公式为成交订单数/用户浏览数),但是该类目仅在浏览域存在,在交易域不存在,那么显然缺失了分子,此类目的转化率就无法计算。
上述的跨主题交叉分析,在维度建模中称为横向钻取(相对于向下和向上的纵向钻取)。 维度一致性对于数据集市集成为数据仓库起着关键作用,实际数据集市设计和开发过程中,必须保证维度一致性,具体可以采用共享同一个维度表或者让其中一个维度表是另外一个维度表的子集等方式来保证一致性,从而避免孤立数据集市的出现。
3.4维度整合和拆分
实际维度表设计中,有时候会出现同一个维度表来自多个前台业务系统的问题,此时就会带来维度整合和拆分问题。
前台的业务系统通常是比较复杂的,比如移动端交易系统和PC端交易系统的系统架构和底层数据库、表结构等完全不一致,而且即使PC端相同,也可能由于历史原因或者业务整合、拆分等原因存在多条业务系统,此时就存在维度的整合问题。
在实际整合中,同一个维度的整合需要考虑如下问题。
(1)命名规范:要确保一致和统一。
(2)字段类型:统一整合为一个字段类型。
(3)字段编码和含义:编码及含义要整合为一致,
比如前台A 系统可以用 Y/N 标识商品在线状态,而B系统用 0/1 表示,此时需要为一致的。实际中还可能碰到商品状态A系统有三个,而B系统有四个的情况,此时需要和业务人员或者需求方共同讨论确定整合逻辑。
与整合相对的是拆分。对于大的集团公司来说,以中石化为例,其主业为成品油销售,但是同时其还有中石化加油站的快捷零售店(在此仅做说明问题使用),它们的商品表字段和属性由于业务的不同而存在很大的差异(石油商品和零售店销售的食品、饮料等),此时是否需要用一个统一整合的商品表
在维度建模理论中,对于上述情况通常有两种处理办法。
1) 建一个基础的维度表,此基础维度表包含这些不同业务的共有属性,同时建立各自业务的单独维度表以包含其特殊的业务属性。比如,上述例子就可以建立一个共有的商品维度表记录商品价格、商品描述等共有属性字段,同时建立成品油销售的商品维度表记录油标号(92、95、97等)等成品油独特的商品属性,另外建立一个零售商品维度表记录便利店的各种商品属性(实际操作中通常先建立两个单独的维度表,然后基于单独维度表生成共有的商品维度表或者视图)。
2) 拆分,即不合并,即各个业务差异独特性的业务各自建立完全独立的两个维度表,各自管理各自维度表和属性。
实际操作中,对于业务差异大的业务,偶合在一起并不能带来很大的便利和好处,因此通常倾向于拆分(既不合并),各自管理各自的维度表。对于业务相似度比较大的业务,则可以采用上述的第一种方法。
3.5维度分类
3.5.1退化维度
所谓退化维度,是指在事实表中那些看起来像是事实表的一个维度关键字,但实际上并没有对应的维度表的字段。退化维度一般都是事务的编号,如购物小票编号、发票编号等。退化维度在分析中通常用来对事实表进行分组,比如通常购物小票编号可以把顾客某次的购买行为全部关联起来,同时分析人员也可以统计用户的购买频次等。
3.5.2行为维度
行为问题是基于过去维度成员的行为进行分组或者过滤事实的办法。行为维度即将事实转化为维度,以确保获得更多的分析能力。
对于某零售公司来说,考虑这样一个问题,年购买频次超过30次或者年购买额达到10万的顾客,和年购买频次1次或者购买额在1000元一下的顾客,他们得到的折扣是相同的吗?营销活动中对于他们的处理应该一致的吗?实际商业实践中,常基于顾客过去的购买行为进行评级(普卡、银卡、金卡、白金卡等),并基于其评级用户给予不同的权益(如商品折扣、免排队等),行为维度通常会缓慢变化,因此需要采用缓慢变化维进行处理。
3.5.3角色维度
角色维度指的是一个事实表中多个外键指向同一个维度表。
考虑银行的房屋抵押贷款业务,通常顾客的一次抵押贷款至少会涉及两个银行角色:首先是客户经理,其负责收集客户资料并和客户沟通等;其次是审批人,其负责审核客户递交的各种资料并决定是否审批通过。这样在抵押贷款事实表中将存在客户经理和审批人两个外键,同时指向员工维度表,即一个维度扮演了两种角色。
当事实表和维度表存在上述多对一关系时,没必要为维度表建立多个副本,只需基于维度表建立多个视图即可。
3.5.4杂项维度
杂项维度一般由前台业务系统中的指示符或者标志字段组合而成。在对业务活动建模过程中,经常会发现在定义好各种维度后,还剩下一些在小范围内取离散值的指示符或者标志字段。例如支付类型字段包括现金和信用卡两种类型(在源系统中他们可能是维护在类型表中,也可能直接保存在交易表中),一张事实表中可能会存在好几个类似的字段。如果作为事实存放在事实表中,会导致事实表占用空间过大;如果单独建立维度表,外键关联到事实表,会出现维度过多的情况。如果将这些字段删除,则更不可行。
这时,通常的解决方案就是建立杂项维度,将这些字段建立到一个维度表中,在事实表中只需保存一个外键。几个字段的不同取值组成一条记录,生成代理健,存入维度表,并将该代理健保存至相应的事实表字段。建议不要直接使用所有组合生成完整的杂项维度表,在抽取时遇到新的组合时生成相应记录即可。杂项维度的ETL过程比的维度略微复杂。
3.5.5微型维度
微型维度的提出主要是为了解决快变超大维度问题。
以客户维度为例,如果维度表中有数百万、千万甚至以亿计的记录,而且这些记录的字段又经常变化,则将这样的维度表一般称为快变超大维度。对于快变超大维度,设计人员一般不会使用插入新的维度行的缓慢变化维处理方法,因为本来超大的维度表中添加更多频繁变化的行即不划算也不合适。
解决的方法是,将分析频率比较高或者变化率比较大的字段提取出来,建立一个单独的维度表。这个单独维度表就是微型维度表。
微型维度表有自己的主键关键字,这个关键字和原客户维度表的关键字一起接入事实表。有时为了分析的方便,可以把微型维度的关键字的最新值作为关键字接入客户维度表。
3.5.6多值维度和属性
当事实表的一行涉及维度表的多行时,会产生多值维度。同样,当维度表的一行需要获取单一属性的多个值时,也会产生多值维度。
考虑银行账户交易明细事实表,通常一个银行账户的拥有人只有一个,但是银行实际上也支持联名账户,如果是联名账户,那么在账户交易明细事实表中账户的维度应该存那个账户人呢?这就是多值维度需要解决的问题。
通常有两种办法可以解决多值维度或者多值属性。
1) 扁平化多值维度: 在事实表中引入多列,比如联名账户最多三人拥有,那么就在账户交易事实表中引入三个账户拥有人列(比如第一账户拥有人、第二账户拥有人和第三账户拥有人)。此外考虑到业务的可变性,可同时添加预留字段(比如第四、第五账户拥有人)。
2) 桥接表: Kimball 的维度建模理论也提出了采用桥接表方式来解决此问题。所谓桥接表、是指在事实表和多值维度表之间新增一个表,这个表起到桥接的作用,所以叫桥接表。比如上述多账户的例子,可在事实表和账户人维度表之间添加一个桥接表,这个桥接表将账户人分组,并用分组主键和事实表关联,用分组中的账户人和实际的账户维度表关联。
但是在实际项目操作中,很少使用桥接表的方式来解决多值问题,因为桥接表是一把双刃剑。桥接表无疑提供了强大的能力和灵活性,但是在这后面是引入的复杂性和巨大的使用风险,客户使用成本很高,而且极易用错产生多重计算,因此很多实际项目中都不推荐使用桥接表。
此外,用于多值属性来说,除了上述两种解决办法,还可以将多值的属性存储到一个大字段内,并用指定的分隔符方便下游提取和使用。
四、深入事实表
事实表是维度建模的核心表和基本表。事实表存储了业务过程中的各种度量和事实。而这些度量和事实正是下游数据使用人员索要关心和分析的对象。
本节将重点探讨事实表,包括事实表的三种主要类型: 事务事实表、快照事实表和累计快照事实表。除此之外本书还介绍了一种特殊的事实表----无事实的事实表,最后还将讨论事实表的聚集和汇总。
4.1事务事实表
事务事实表是维度建模事实表中最为常见、使用最广泛地事实表。
事务事实表通常用于记录业务过程的事件,而且是原子粒度的事件。事务事实表中的数据在事务事件发生后产生,数据的粒度通常是每个事务一条记录。一旦事务被提交,事实表数据被插入,数据就不再进行更改。通过事务事实表存储单次业务事件/行为的细节,以及存储与事件的维度细节,用户即可单独或者聚合分析业务事件和行为。
事务事实表的粒度确定是事务事实表设计过程中的关键步骤,一般都会包含可加的度量和事实。理解概念的最佳途径无疑是实际的例子,因此下面将结合超市零售业务以及维度建模的四个环节来说明事务事实表。
4.1.1选择业务过程
在超市的零售示例中,业务用户要做的事情是更好地理解POS 系统记录的顾客购买情况,那么很容易确定业务过程就是POS系统,即在什么时候,什么商品、哪个收银台、销售了哪些产品等。
4.1.2定义粒度
顾客单词购买行为的体现是一张购物小票,但正如上述所言,事务事实表应该选择最原子粒度的事件,所言小票的子项(在业务上的动作即为收银员每次扫描的商品条码)应是超时零售事务表的粒度。
4.1.3确认维度
小票子项的粒度确定后,销售日期、销售商品、销售收银台、收银员、销售门店等维度很容易被确定了。另一个不太容易考虑到的维度是促销行为,但是通过和业务人员交流或者查看报表表头等也能够发现此维度。
4.1.4确定事实
维度设计的最后一步,是确定哪些事实和度量应该在事实表中出现。对于本例,商品销售数量、销售价格和销售金额很容易确定下来。但是实际上,商品的成本价是确定的,因此可以很容易地确定商品的销售毛利:(商品实际销售价格-商品成本价)x 销售数量,基于下游使用便利这一因素,也应该将此放入事务事实表中。
基于毛利润也可以计算出毛利率,那么毛利率这种比例应该放入事务事实表吗?在事实表的设计中,一个常见的原则是指存放比例的分子和分母,因为比例的计算是和业务强相关的,业务逻辑可能比较复杂而且比例是非可加的,所以一般不将比例的计算放入事实表中。
4.2快照事实表
在实际的业务活动中,除了关心单次的业务事件和行为外,很多时候还关心业务的状态(当前状态、历史状态)。以超市零售业务为例,管理人员和分析人员除了关心销售情况,还会关心商品的库存股情况,例如哪些商品库存告罄需要补货、哪些积压需要促销,而这正是快照事实表(周期快照事实表)所需要解决的范畴。
所谓周期快照事实表,是指间隔一定的周期对业务的状态进行一次拍照并记录下来的事实表。最常见的例子是销售库存、银行账户余额等。
与事务事实表的稀疏性不同(这里的稀疏性是相对的),周期快照事实表通常被认为是稠密的。因为事务事实表只有事务发生才会记录,但是周期快照则必须捕获当前每个实体的状态。比如,某个商品如果某天没有销售,那么这个商品是不会存在于当天的事务事实表中,但是为了记录其库存情况,即使没有销售行为,也必须在周期快照事实表中对其拍照。
周期快照事实表的周期通常需要和业务方共同确定,最常见的周期是天、周和月等。
周期快照事实表中的事实一般是半可加的,入某个商品的库存可以跨商品、仓库等相加,但是明显在时间上相加是没有意义的。
下面以超市的库存业务为例来介绍周期快照事实表的设计过程。
4.2.1选择业务过程
本例是为了更好地理解超市的库存情况,因此业务过程就是商品的库存情况,即在什么时候、什么商品、那个仓库的库存量如何。
4.2.2定义粒度
这里的粒度主要指库存的周期,商品的粒度很容易确定(需要注意这里的商品是SKU级别,而不是商品型号级别,比如一双特定型号的鞋子有尺码,每个尺码还有颜色,这里的库存显然应该在SKU级别,即某个型号的、某个尺码的、某种颜色的鞋子库存)。选择库存的周期需要考虑到数据量的膨胀情况。考虑如下例子,某个超市有1万个商品(即SKU),其中有100家连锁店,那么对其库存拍照将有100X10 000 = 100万行记录,那么一年将有 365* 1000 000=3.65亿条记录。当然随着目前存储的日益廉价,这些都不是问题,但是设计人员需要考虑到这些因素。
4.2.3确认维度
对于超市零售库存,相应的维度为周期(天、周、月等)、商品、仓库(总仓、分仓或者门店等)。
4.2.4确定事实
这里的事实很容易确定,即库存量。但是仅仅记录现存库存量是不够充分的,因为业务上通常会和其他事实协同来度量库存的变化趋势、快慢等,所以还可对周期快照实施表的事实进行增强,常见增量度量包括库存价值(库存量*销售价格)、库存成本(库存量*成本价格)、周期销量(库存周期内销量)和去化天数(基于周期内销量预计需要的库存售罄天数)等。
4.3累计快照事实表
事实表的第三种类型是累计快照事实表,相比前两者,累计快照事实表没那么常见,但是对于某些业务场景来说非常有价值。
累计快照事实表非常适用于具有工作流或者流水线形式业务的分析,这些业务通常涉及多个时间节点或者主要的里程碑事件,而累计快照事实表是从全流程角度对其业务状态的拍照。
考虑车险理赔业务,一次车险的理赔通常包括客户报案、保险公司立案、客户提交理赔材料、理赔审批通过和付款等关键步骤,而累积快照事实表正是从全流程角度对每个车险理赔单的拍照,拍照内容即是其关键步骤的各个状态,便于业务人员一目了然地分析各个理赔单的状态、步骤间的耗时等。
下面以车险理赔业务为例来介绍累计周期快照事实表。
4.3.1选择业务过程
本例是为了更好地理解保险公司得车险理赔业务,因此业务过程就是车险理赔,即在什么时候、哪个理赔申请所处的状态如何。
4.3.2定义粒度
累计周期快照实施表的粒度一般很容易确定,就是业务的某个实体,这里即为保险理赔申请。
4.3.3确认维度
对于累计周期快照事实表,相关的维度包含快照周期(天、周、月和年等)、理赔申请人、受理人、审核人、网点(电话或者实体)等。
4.3.4确定事实
这里的事实包括索赔金额、审批金额、大款金额、处理时长等。
4.4无事的实事实表
在维度建模中,事实表是过程度量的核心,也是存储度量的地方。但事实表并不总是包含度量和事实。这类不包含事实的事实表被称为无事实的事实表。
乍听有点奇怪,但是请考虑下面业务场景,银行客户服务中心接受客户电话咨询或者在线业务咨询,这里并没有任何的业务度量值,唯一的度量值就是单次咨询事件。
其他类似事件还有学生课程出现情况、用户在网站上的浏览行为、客户对广告的点击行为。
无事实的事实表通常人为增加一个常量列(其列的值总是为1)来方便对业务事件的统计分析。
4.5汇总事实表
尽管在当今的大数据时代,计算和存储的代价越来越低,但不代表是没有代价的。处于对性能以及下游使用便捷性的考虑,数据仓库还经常对事实表预先进行聚合和汇总。
通过仔细的规划和设计,汇总的事实表能够给计算和存储的成本、数据仓库的性能带来很大的收益。尽管聚集和汇总能带来良好的收益,但是也需要付出代价。代价就是带来额外的聚集和汇总任务的维护,尤其是上游明细有任何改动或者更新,如果需要重新更新汇总的事实表,那么重刷事实表的代价也是比较大的。
实际项目中,常常根据业务需求的频繁性来确定需要聚集的维度。此外,为了保证数据的一致性,汇总的事实表通常基于明细表的维度和事实进行计算,以保证维度和计算口径的一致。
五、维度表
大数据时代对于维度表设计改变最为明显的是缓慢变化维的处理。在传统缓慢变化维的处理中,需要根据实际情况选用3.1 的三种方法或者其组合等来处理维度的缓慢变化情况。大数据时代的处理则更为简单和直接,既然存储已经变得廉价,那么为什么不讲维度表的快照每天存储一份呢?毕竟相对事实表来说,维度表所占用的存储小的很多。
用维度表快照的方式来处理缓慢变化维,实际上也是用存储的冗余开销换来了缓慢变化维复杂逻辑的消除以及下游使用的便捷。想想传统方式要捕获每个维度字段的缓慢变化以及维护对应在事实表代理键的处理复杂性,还有给下游维度表使用所带来的困惑和使用成本,快照的缓慢变化维处理方式还是非常值得的,因为相应的ETL 逻辑要简化很多,而且下游使用也更为直接和方便。
用快照方式处理缓慢变化维还直接带来了微型维度的消除,因为不管是维度的缓慢变化,还是微型维度要解决的维度频繁变化,快照的方式已经包含了所有历史变化。
大数据时代对维度表设计改变的还有维度层次、杂项维度以及多值维度/属性。大数据对这些维度设计的处理时扁平化和反规范化。
维度层次的扁平化也就是在单一维度表中用冗余字段来存储所有层次,维度有5个层次就用5个层次字段,有10个就用10个层次字段,存储和成本不是问题。
杂项维度和多值维度/属性同样也是多字段解决方案。比如前文所述的账户交易事实表对应多个账户人的问题,有几个账户人就用几个账户字段(当然实际中可以加以限制,比如通过大字段存储超过2个的其余共同账户人),多值属性也可以通过类似方案来解决(当然也可以通过大字段的方式来解决,比如把多值属性组装成键值对放在一个长字符串内。)