关于缓慢维度变化的简介
在数据仓库设计中理想状态下维度是不会变化的,但是事与愿违,大部分业务系统中的维度是会发生变化的,那么如何去处理这些变化的维度就是本文关注的内容,举个例子如下企业组织架构维度表
ID_ | 企业名 | 部门名 | 组名 |
---|---|---|---|
1 | 微软 | 研发部 | 开发一组 |
如果有一天上表中的部门名发生变化怎么办?根据Kimball的数据仓库工具箱,我们把处理缓慢维度的方法分为7种类型,其为类型1至类型7
类型1:重写
顾名思义,就是将维度表中的字段简单的替换,如上表中如果采用类型1的话,如果部门后续由研发部变成技术部,那么维度表将变成下表,部门维度表和部门人数事实表做关联
ID_ | 企业名 | 部门名 | 组名 |
---|---|---|---|
1 | 微软 | 技术部 | 开发一组 |
部门人数事实表
ID_ | 部门ID外键 | 人数 |
---|---|---|
1 | 1 | 10 |
- 类型1的优劣势
有点是简单易于实现,仅需重写维度表中的属性值而不需要去更改事实表,但是无法保留历史值,如果业务中存在对维度的历史值进行分析,那么就无法采用该类型,举例:采用类型1将部门名改成技术部后,现在需要分析在部门叫研发部时,研发部下有多少人?那么如果采用类型1将无从下手,因为维度表中没有保存历史维度数据,而部门人数事实表中的数据是用部门ID去与其关联的。但是实际中如果采用类似hive无法执行update语句的数据仓库工具去建设的话,那么只能重新全量生成维度表
类型2:增加新行
类型2是最常用的处理缓慢维度的办法,如果采用此设计方式则在部门名变更为技术部时维度表和事实表如下
ID_ | 企业名 | 部门名 | 组名 | 行有效日期 | 行失效日期 | 是否生效 |
---|---|---|---|---|---|---|
1 | 微软 | 研发部 | 开发一组 | 2011-1-1 | 2019-9-1 | no |
2 | 微软 | 技术部 | 开发一组 | 2019-9-2 | 9999-1-1 | yes |
部门人数事实表
ID_ | 部门ID外键 | 人数 |
---|---|---|
1 | 1 | 10 |
2 | 2 | 100 |
在事实表中原来旧的数据仍然和研发部关联,但是新增的数据已经和新的维度(技术部)做关联。这样数据就保留了历史变化的信息,如果需求中存在对历史维度进行分析的话可以采用此类型应对维度缓慢维度变化。
类型3:新增新列
采用维度2去处理维度变化时存在一个缺点,如果需求需要将新的属性值和旧的属性值做关联分析,那么采用类型2将很难实现,因为类型2并没有记录维度变化的关联关系,如果存在这方面需求则可以采用类型3处理,事实表通过代理键ID关联维度表(维度表在创建时自己生产的id,不是业务方提供的ID)但实际中类型3不经常使用 如:
ID_ | 企业名 | 当前部门名 | 之前部门名 | 组名 |
---|---|---|---|---|
1 | 微软 | 技术部 | 研发部 | 开发一组 |
部门人数事实表
ID_ | 部门ID外键 | 人数 |
---|---|---|
1 | 1 | 10 |
类型4:微型维度
其解决问题的场景为应对维度表中存在大量数据(几百万),且一些属性快速变化。因为当维度表中存在大量的数据,特别是维度中某些信息还是易变时,我们不希望已经存在几百万的维度表中新增数据(采用类型2),因为这样会使维度表发生膨胀,实际情况中维度表中的数据应该远远小于事实表,那么类型3就派上用场,还是引用上诉两张表,假设现在部门维度有几十万个部门,且组名经常发生变化(即使不太可能),那么我们将才用下面设计
部门表
ID_ | 企业名 | 部门名 |
---|---|---|
1 | 微软 | 研发部 |
组名表
ID_ | 组名 |
---|---|
1 | 开发一组 |
1 | 开发二组 |
部门人数事实表
ID_ | 部门ID外键 | 组名外键 | 人数 |
---|---|---|---|
1 | 1 | 1 | 10 |
2 | 1 | 2 | 100 |
- 部门表和组名表不再使用星型维度直接做关联,而是在事实表中新建外键来关联部门和组
类型5:微型维度与类型1支架表
之所以称之为类型5是因为其是类型4和类型1的结合 4+1=5,该技术实现为在主维度中用一个外键id和一个微型维度做关联,这样能避免微型维度发生变化时更新主维度的每行的信息,且如果客户需求需要在不关联事实表的情况下完成维度的过滤分析等类型5是很好的处理方法,举例
商店维度表
ID_ | 商店名 | 地区ID |
---|---|---|
1 | 一号门店 | 1 |
地区维度表
地区ID | 省 | 市 | 县 |
---|---|---|---|
1 | 福建省 | 福州市 | 闽清县 |
如果不采用类型5的话维度表结构为
ID_ | 商店名 | 省 | 市 | 县 |
---|---|---|---|---|
1 | 一号门店 | 福建省 | 福州市 | 闽清县 |
2 | 二号门店 | 福建省 | 福州市 | 闽清县 |
那么有一条闽清县更改了名字变成闽清市,如果不采用类型5支架表的话那么将要更改所有商店维度表中有关闽清县的记录,将其改成闽清市,如果你在很多事实表中都使用了地区信息,那就需要更改所有有关地区的维度,这是无法忍受的,但是如果采用类型5则只需要更改地区类型的属性即可完成所有有关地区维度信息的修改。
类型6
类型6为 类型2,3,1的组合(2+3+1=6),回顾类型3,当为了记录维度变化的历史先后关系时我们采用类型3,但是类型3也有一个缺点就是它只记录当前的变化,和上一次的变化,类型6解决了这个问题,其维度表如:
ID_ | 企业名 | 当前部门名 | 之前部门名 | 组名 | 生效日期 | 结束日期 | 当前是否生效 |
---|---|---|---|---|---|---|---|
1 | 微软 | 技术部 | 研发部 | 开发一组 | 2011-1-1 | 2019-9-1 | no |
2 | 微软 | 技术部2 | 技术部 | 开发一组 | 2011-1-1 | 9999-1-1 | yes |
部门人数事实表
ID_ | 部门ID外键 | 人数 |
---|---|---|
1 | 1 | 10 |
- 尽管该技术具有某种诱惑力,但是加大了系统的复杂度,考虑任务是否有这种需求才使用这种方法。
类型7
当需要分析维度表的当前和历史场景的时候可以使用类型7,其运用事实表关联类型2和基于类型2的维度表创建一个当前部门的视图来实现,如下
部门维度
ID_ | 部门编码 | 企业名 | 当前部门名 | 生效日期 | 结束日期 | 当前是否生效 |
---|---|---|---|---|---|---|
1 | code1 | 微软 | 研发部 | 2011-1-1 | 2019-9-1 | no |
2 | code1 | 微软 | 技术部 | 2011-1-1 | 9999-1-1 | yes |
当前部门视图(可以根据部门问题创建一个当前部门视图 比如:select * from 部门维度 where 当前是否生效=’yes’)
部门编码 | 部门ID | 当前部门名 |
---|---|---|
code1 | 2 | 技术部 |
code1 | 3 | 人力资源部 |
部门人数事实表
ID_ | 部门ID外键 | 部门编码 | 人数 |
---|---|---|---|
1 | 1 | code1 | 10 |
2 | 2 | code1 | 10 |
这样通过事实表中存在,部门ID外键和部门编码就可以分析历史部门数据和当前部门数据所存在的关系
杂项目维度
在建模过程中通常会遇到大量五花八门的指标和描述,它们包行小的范围离散值处理这些离散值有一下几种方法
- 忽略这些标志和指标,如果这些描述具有业务价值则不能采用这种方式
- 在事实表中保存这些表示,尽量不要在事实表中保存这些描述,这些描述可能使事实表的列发生膨胀
- 将每个标志和指标放入各自的维度,如果事实表的外键在合理范围内(不超过20个),则在事实表中加入不同的外键是可接受的。
- 杂项维度,如下
在交易事实表中存在以下杂项描述
支付方式:CASH,CREDIT
购买渠道:ONLINE,OFFLINE
对应事实表中一共存在以下4中组合方式(2的二次方),数据为支付方式 购买渠道 CASH ONLINE CASH OFFLINE CREDIT ONLINE CREDIT OFFLINE
那么与其建这些杂项目反正事实表中,或者维度数目已经较大的情况下,我们可以创建一个杂项维度,将这些杂项的所有可能罗列出来如
ID | 支付方式 | 购买渠道 |
---|---|---|
1 | CASH | ONLINE |
2 | CASH | OFFLINE |
3 | CREDIT | ONLINE |
4 | CREDIT | OFFLINE |
现在在事实表中的数据为
ID_ | 杂项外键 | 金额 |
---|---|---|
1 | 2 | 100 |
2 | 1 | 1000 |
不同粒度的事务事实(表头模式)
实践中应该避免这么模式,应该保证事实表中的粒度和维度是一样的
比如存在订单详细事实表
ID_ | 订单ID | 商品外键 | 金额 |
---|---|---|---|
1 | order1 | 1 | 1000 |
2 | order1 | 2 | 888 |
订单维度表
ID_ | 订单描述 |
---|---|
order1 | 订单1信息 |
order1 | 订单2信息 |
这样的设计存在一个问题,订单详情事实表中的粒度是订单的每个商品,但是却关联了订单维度,这两个关联存在不同的粒度,应该尽量避免这种情况,更好的做法应该是再新建一个关于订单的事实表,在订单明细表中将订单id做为退化维度(只是单纯的记录一下订单id,为了方便分析)
多币种
最常见的分析需求是订单事务以当地交易币种表示,在构建订单事实表时应该包含2种货币信息事实,一种是当地货币,一种是公司采用的标准统计货币,比如公司在日本销售的货物以日元结算,公司财报中的统计标准为美元,那么在订单事实表中应该包行2种货币,一种是日元,一种是公司的标准货币美元。