数据仓库中的事实表总是在变化中,通常是新的业务数据不断装载入DW。事实表数据的增加是正常现象,也无需特别处理。但很多时候维度表的数据也会发生变化,且维度表的数据变化会导致维度表和事实表的关系发生变化。因此对于维度变化,该如何设计维度表,是本文要讨论的问题。
例如一个订单事实表和一个客户维度表,每个订单有一个对应的客户。在今年5月17日之前,客户维度表中某客户的类别是corporate,5月18日变化为了retail。很显然统计5月的订单数据时,我们希望得到的客户类型即有corporate,也有retail。这就要求维度表中保存历史数据,同时如何关联事实表和维度表也是需要设计时思考的问题。
Slow Changing Dimensions(SCD)
指与时间等因素无关的,变化相对较慢的维度。如产品类别维度,地区维度等。
SCD可根据是否/如何保留维度的变化历史,分为不同类别。
Type1:
将旧的维度信息覆盖。如下例,当客户类别信息发生变化时,直接将维度表中的旧值覆盖掉。因此Type 1没有保留维度的变化历史。
这种方式的优点是简单,缺点是无法追踪历史。如果是雪花模型,还可能会导致一些外键关系错误。
Type2:
增加一行新的维度数据。如下例,客户维度表中新增一行数据,并将旧数据行标记为“过期”。注意维度表中一般有唯一标识,这里是customer_id,因此新增的数据id不能也为1.
Before the change:
After the change:
如上所示,该方法保留了旧的历史数据。优势是根据start/end date可以追踪所有的历史,且我们知道变化的具体时间点。缺点是在维度表中引入多条历史记录导致数据量增大,效率降低。同时由于唯一标识的变化,可能导致事实表和维度表的关系变得不一致,导致错误的查询结果。
因此对于可以使用Type3的情况,一般不推荐使用Type2.
Type3:
添加一个新字段来存储旧的维度数据。如下例,增加一个previous_type字段。
Before the change:
After the change:
缺点是仅仅能保存上一次历史数据,对于多次变化的历史则无法追踪。同时由于不知道变化发生的具体时间点,我们也失去了对一些数据的分析能力。例如我们希望得到截止到5月15日类型为corporate的客户共发生了多少个订单,在SCD Type3则无法统计。因此在Type 3中我个人认为可以加上一个Change_Date字段。
但Type3在大多数场景中已经够用,且由于效率较高,因此很常用。
Type1,2,3是常见的设计方法。
Type4:
新增一个专门用于存储历史数据的历史表。维度表本身还是仅仅保存当前信息。
Current table:
Historical table:
上例中可见维度历史表可以重复的唯一标识列。该设计方法的优点是效率高,可以仅在需要历史数据时才使用历史维度表。
Type6:
Type6结合了Type1,2,3。下例中使用Type1将current_type全部覆盖为新值;使用Type2增加新行;以及Type3增加新的字段historical_type。
但是我不清楚这种设计的优势是什么。
Rapid Changing Dimensions(RCD)
指那些与时间有关,或与数据输入相关的,变化相对较快的维度。例如在一个分析用户如何使用搜索引擎的DW项目中,将用户搜索的关键字作为一个维度。由于用户使用的关键字会快速变化,因此关键字维度中的数据量会迅速增加。另外一个例子就是精度为秒的时间维度,每秒就会增加一个维度值。
通常RCD可以分为3类:
Rapidly Changing Small Dimensions -- 即维度表字段并不多,表的数据量也不大的情况。这种情形应用SCD中的Type2就可以了。
Rapidly Changing Large Dimensions -- 即维度表字段较多,表的数据量较大的情况。这种情形Type2会增加过多的行并导致效率降低,因此通常采用Type3.
Rapidly Changing Monster Dimensions -- 最糟糕的情况,即维度表字段较多,表的数据量很大,且维度表中的一部分字段频繁变化的情况。此时应将相对稳定的字段和频繁变化的字段分割开,频繁变化的字段独立出来形成新的维度表与事实表相连或形成新的雪花关系。
最后为一篇很不错的关于维度变化的论文:
http://www4.di.uminho.pt/~prh/uce15-0809/g41.pdf