前言
支架表是维度设计中非常有意思的一部分,可以说是星型模型和雪花模型的结合;但在大部分维度建模书里都只是简单的一笔带过,实在是过于可惜。
在本文,笔者会对支架表进行详细的介绍,并就其实际应用场景进行探讨。
支架表的诞生
支架表的诞生离不开经典的数仓模型之争——星型模型与雪花模型
星型模型
简单地说,所有的维度表都连在1个事实表上,就是星型模型
星型架构是一种非规范化的结构,多维数据集的每一个维度都直接与事实表直接连接。
所以数据必然存在一定的冗余。
雪花模型
当有一个或多个维表没有直接连接到事实表上,而是通过其他维表连接到事实表上时,其图解就像多个雪花连接在一起,故称雪花模型。
雪花模型是对星型模型的扩展。它对星型模型的维表进一步层次化,原有的各维表可能被扩展为小的事实表,形成一些局部的 " 层次 " 区域,这些被分解的表都连接到主维度表而不是事实表。
对于研发出身的人来说,雪花模型更符合他们的审美——符合范式且不会存在数据的冗余。
雪花模型虽然看起来的确舒服,但在数仓中——尤其是Kimball模型——基本无人采用,其对于开发人员的技术要求、ETL的开发、后续的运维都过高;当然主要是没这个必要搞那么复杂。
支架表
虽然上面也提到了,雪花模型不怎么靠谱。但星型模型的冗余有时候的确会有点太多了。
虽然我们不推崇雪花模型,但如果一组属性在维度表中出现不止一次时,我们也可以采用受限的雪花模型——也就是支架表。
可以看到,冗余被大量减少了,结构也看起来舒服多了。
支架表的使用场景
让我们先回顾一下支架表的定义
当一个属性集合(例如日期、地点)在某个维度或多个维度表中反复出现时,就可以考虑使用支架表。
正如 Kimball 自身给出的定义,支架表不能乱用——用多了不就是另一个雪花模型吗?只有在以下条件的时候再考虑:
在单个维度表中反复出现该支架属性时
例如雇员维度表中,同时调用了 出生日期属性 和 入职日期属性 两个日期属性值。
被调用的属性值较多时
即使被反复调用,如果每次都只有1、2个属性值。那笔者还是推荐保留星型模型;不过日期、地点这种太多的就算了。
被多个维度、事实表调用,且被调用时的属性值定义完全相同
例如日期、地点
基本不需要修改或修改频次极小
正如雪花模型的ETL令人头痛一样,支架表的ETL也极为复杂(下面详细说)。
解决一个东西变化修改复杂的最好办法就是避免它变化。
支架表的注意要点
例如时间表——稳定在 24 * 60 * 60 行(粒度在秒时)
更多的表关联
显而易见,支架表的使用会增加查询时的关联数量。一方面会造成查询复杂性的增加。另一方面存在降低性能的风险。
如果数据量不大,可以考虑写个视图来完成维度表和支架表的关联。
缓慢变化维的处理
使用支架表时,一定要特别注意缓慢变化维的相关规则。
例如类型2,有时即使维度行并未发生变化,但也可能需要其对应用类型2变化——因为支架表的类型2变化。
我们举一个简单的例子。
假定location支架表有一个业务地点名称叫 “XR门诊中心”,突然有一天这个地点名称更改为“XR专科门诊”。业务规则规定以前的业绩还是计入之前的名称,但改名后的业绩需要用新的名称来统计。
那我们就需要使用类型2变化来响应,在Location表里添加新行——包含代理键和新的地点名称。同时在user表里添加新行,其中location的代理键自然使用新值。
也可以考虑使用类型1处理——如果业务接受的话。
字段命名的问题
当一个维度对相同的支架表有多个关联关系时(大概率存在),再仔细的开发人员也会犯错。
数仓要求 字段定义——字段名称 最好保证完整的一致(也就是总线矩阵中的一致性维度)。但在维度表和支架表中显然不可能实现——就是会反复调相同属性才会使用支架表。因此这里的字段命名设计、ETL设计、元数据维护、文档管理都尤为的重要。
常见的支架表
日期-date
日期支架表应该是最常用的支架表了——尽管很多时候使用者并不知道这叫支架表
字段名 | 字段类型 | 注释 | example |
---|---|---|---|
day_key | int | 代理自增ID,用于对外关联 | 1 |
full_date | date | 完整日期 | 2020-8-21 |
day_of_week_number | int | 该周第几日 | 1 |
day_of_week_en_name | char | 该周第几日英文 | monday |
day_of_week_ch_name | char | 该周第几日中文 | 周一 |
day_of_month | int | 该月第几日 | 12 |
holiday_flag | char | 是否节假日 | 是 |
weekday_flag | char | 是否周内 | 是 |
weekend_flag | char | 是否周末 | 否 |
month_en_name | char | 所属月英文名 | May |
month_ch_name | char | 所属月中文名 | 五月 |
month_number | int | 所属月 | 5 |
year | int | 所属年 | 2020 |
quarter | int | 所属季度 | 3 |
日期支架表是最常用的支架表,字段I自然不止这十几个。限于篇幅展示这些,具体内容建议结合业务之余百度。
时间-time
时间支架表经常被人忽略,但却是笔者心中最完美的支架表——永不变化,永远稳定
字段名 | 字段类型 | 注释 | example |
---|---|---|---|
time_key | int | 代理自增ID | 1 |
second | int | 秒 | 59 |
minute | int | 分 | 59 |
hour | int | 时 | 23 |
time_range | char | 时间段 | 下午 |
am_flag | char | 是否中午12点前 | 是 |
pm_flag | char | 是否中午12点后 | 否 |
时间支架表被忽视的最主要原因就是不常用......
地点-location
location表的风险点,在于它的更新频率并不低,例如国家 撤县划区 。这种情况下就需要考虑缓慢变化维的处理了。
字段名 | 字段类型 | 注释 | example |
---|---|---|---|
location_key | int | 代理自增ID | 1 |
location_id | int | TP系统的自然键 | 1 |
gov_code | char | 官方的地址编码 | 610502 |
district | char | 所在区 | 临渭区 |
city | char | 所在地级市 | 渭南市 |
province | char | 所在省 | 陕西省 |
country | char | 所在国 | 中华人民共和国 |
post_code | char | 邮政编码 | 714000 |
location表深究起来字段亦是非常多的,这里只列部分。
其他杂谈
业务驱动
支架表乃至整个数仓的搭建时,都要从业务角度来思考。甚至可以在最后一步再调研已有系统——如果定义的指标、维度合理而目前研发不能满足,那我们需要的是研发补充而非修改指标定义甚至放弃这个指标。数据是产生于各个系统,但产生作用永远是在业务方面。
不要节省资源
很多建模师推荐使用范式建模而非维度建模,这种模型师无疑是优秀的技术架构师——毕竟这年头还有很多系统连范式都没有——但毫无疑问不是优秀的数据架构师。从数据的角度,数据库容量永远是最后考虑的东西。
或者换个角度,告诉老板你可以提升BI展示5倍的速度、日常写SQL效率10倍,但需要目前4倍的硬盘容量。对老板来说这连选择题都不是。
星型模型与雪花模型
或者更准确的说,是维度建模与范式建模。纯粹的范式建模不适用与OLAP系统,纯粹的星型模型也会遇到无数的苦难。根据实际业务情况(而非底层系统)进行适当的妥协——例如微型维度和支架表——才能使你的模型真正靠谱。