在维度建模过程中,维度表的设计和生成是非常重要的一环。维度表中的内容反映的是事实发生的环境,对数据仓库系统可解释性和可用性起到了很重要的作用。可以说,维度表设计的好坏很大程度上决定了数据仓库的成败。在维度表的设计和字段的解释说明上,是值得投入大量的精力的。
在实际的设计过程中,需要考虑的问题有很多,这篇文章提的三点在工作中比较常见和重要的。
1. 一致性维度
Kimball在他的书中提到了一个概念是“企业数据仓库总线架构”,是一种构建企业数据仓库的方法,其核心基础就是一致性维度。一致性维度的简单说就是共享维度,对公司内所有的业务或者说事实表,统一的进行维度设计。在实践过程中,不仅包括,不同的事实表使用相同的维度表,还有不同的维度表使用相同名称相同含义的维度字段。例如,商品维度表中跟品牌相关的字段要跟品牌维度表完全一致。
一致性维度一个显著的好处,就是可以跨事实表分析,可以简单的把不同事实表中的信息合并到同一张报表中。
2. 维度的粒度
维度信息一般是包含层级关系,每条维度记录对应最细粒度的一条数据。例如商品表中有“分类-品牌-商品”三层信息,每个商品对应一条数据。一般情况下,层级数据是整齐,每个商品都有自己的品牌和分类,这种情况下比较好处理。但是有些时候需要考虑维度深度不同,或者需要可以增加深度缺失记录的情况。
例如,地理位置维度表的深度是参差不齐的,不同级别的城市需要的层级差别很大,在实际使用中,我们并不是把不存在的级别留空,而是对位置维度表重新设计来满足具体的使用情况。
城市id | 城市名 | 区名 | 所属地级市 | 所属省份 |
---|---|---|---|---|
1 | 上海 | 黄浦区 | 上海 | 上海 |
2 | 苏州 | 姑苏区 | 苏州 | 江苏 |
3 | 昆山 | 昆山 | 苏州 | 江苏 |
还有情况是需要额外添加深度缺失的记录,来适应事实中可能的字段缺失。例如,我们有一个页面位置维度表,用来描述页面上每个具体的问题,包括大区域、小区域和位置索引三个层级,但是位置索引并不是在所以的操作源数据中都存在,需要增加深度缺失的记录。
位置id | 大区域 | 小区域 | 位置索引 |
---|---|---|---|
1 | 推荐 | 精彩推荐 | 1 |
2 | 推荐 | 精彩推荐 | 2 |
3 | 推荐 | 精彩推荐 | 未知 |
3. 缓慢变化维
最后说一下缓慢变化维,这个也是维度设计中必须要考虑的问题。业务数据中某些字段会随着时间变化是正常的,数据仓库需要用缓慢变化维的方式来处理这种变化,常见的处理方式有如下几种:
3.1 直接覆盖
根据业务的需求,某些字段的值可能只需要直接修改就好。例如商品名称,如果只是为了在报表中展示用,而名称的修改只可能是文字的优化,那就直接改为新名字就好。但是要注意名称直接变化对统计可能带来的影响,评估基于旧名称生成的数据是否需要重新生成。
3.2 多列属性
多列属性就是通过多列的方式去存储不同时期的值,适用面比较窄,主要是一些变更次数严格限制,或者只关心最近变更的情形。
用户id | 用户名(userName) | 曾用名 (oldUserName) |
---|---|---|
u001 | 张三 | 张小三 |
u002 | 李明 | null |
3.3 增加新行
这种处理方式是每次数据发生变更就增加一行,是一种最常见的处理缓慢变化维的方式。这样会导致同样的一个自然业务主键(例如用户id)会存在多条,那么我们就需要一个更加一般化的键来确定唯一的行,常见有有两种方式来实现。
3.3.1 拉链表
因为每个自然键在维度表中可能有多条,需要额外的字段来标识,一种很容易想到的方式就是增加时间字段,例如
用户id | 用户名 | app版本号 | 生效时间 | 失效时间 |
---|---|---|---|---|
u001 | 张三 | 3.1.2 | 2018-08-16 09:02:44 | 2018-09-10 11:23:01 |
u001 | 张三 | 3.1.3 | 2018-09-10 11:23:01 |
通过生效时间和失效时间字段可以成功的把维度的变化记录下来。但是拉链表在使用的时候存在一个小问题,就是每次在使用的时候都必须在sql中包含生效时间和失效时间的过滤。这个对数据分析人员是一个不算小的负担,并且可能因为忘记导致统计结果的问题。
3.3.2 代理键
代理键的方法就是生成一个新的键作为维度表的唯一键,一般是依次递增的数字。代理键做为跟事实表上跟维度表关联的外键,而不再是自然主键。
代理键 | 用户id | 用户名 | app版本号 | 生效时间 | 失效时间 |
---|---|---|---|---|---|
1 | u001 | 张三 | 3.1.2 | 2018-08-16 09:02:44 | 2018-09-10 11:23:01 |
2 | u001 | 张三 | 3.1.3 | 2018-09-10 11:23:01 |
上面的方式,除了代理键外,依然保留了拉链表中的生效和失效时间,也可以考虑用一个布尔值的“是否是当前生效字段”替代。
代理键的方案可以让分析人员在使用过程中只需要用key关联就好,不需要关心时间字段。但是代理键也并不是十全十美的,可能带来的是自身维护的问题,在ETL中维护自增id带来的成本可能会比想象中的大。
3.4 增加快速变化附属维度
如果维度表的列比较多,大部分的字段几乎不变,少量字段变化相对较多,那么可以考虑附属维度的方式。将变化快速的字段单独组成附属维度,与原有维度相关联,形成的数据结构类似于雪花模式。鉴于之前的文件说明过,并不推荐使用雪花模式,附属维度也应当谨慎使用。