吴乙己的数仓指南_5.1维度建模技巧之SCD2类型缓慢变化维搭建
目录
- 0.引言
- 1.SCD类型
- 2.举个栗子
- 3.SCD2
- 3.1 什么是SCD2
- 3.2 如何使用SCD2
- 3.3 如何实现SCD2
- 3.3.1 获取维度基准
- 3.3.2 按情况分治打标
- 3.3.3更新维度表
0.引言
缓慢变化维技术(Slow Changing Dimension,简称SCD)是维度建模中几乎无时无刻不在使用的技术,它为维度提供了基于历史数据进行切片、切块的能力。
1.SCD类型
以kimball老爷子的理论,共有7种SCD类型,但在日常的数仓开发中,最常使用的是SCD2。所谓SCD2类型,即在维度表中增加新行记录最新维度。
2.举个栗子
假设我们有一张地域维度表,记录了行政区号、地名信息。建表语句如下:
--基于HQL方言
CREATE TABLE DIM_ADD(
ADD_NO STRING COMMENT '地域编码',
ADD_NAME STRING COMMENT '地域名称'
)
其中有一行数据:
ADD_NO | ADD_NAME |
---|---|
010 | 北京 |
我们知道,在100年前,北京还不叫“北京”,叫做“北平”,如果我们要基于100年前的数据进行分析,那彼时的地名字段应写做“北平”。可是我们的维表中没有“北平”,该怎么办呢?
3.SCD2
3.1 什么是SCD2
为了解决第二节的问题,SCD2就是一种非常优雅的办法。
SCD2,Kimball老爷子只总结了两个字——“新增”。
说白了,就是在维度表中保留历史数据,新增最新数据,并以数据的生效时间段来约束数据的有效性。
一张简单的SCD2类型维度表建表语句如下:
--基于HQL方言
CREATE TABLE DIM_ADD_SCD2(
ADD_NO STRING COMMENT '地域编码',
ADD_NAME STRING COMMENT '地域名称',
FROM_DATE STRING COMMENT '起始时间',
TO_DATE STRING COMMENT '结束时间'
)
接着,北平的数据就可以维护进来
ADD_NO | ADD_NAME | FROM_DATE | TO_DATE |
---|---|---|---|
010 | 北平 | 1368-09-12 | 1947-09-27 |
010 | 北京 | 1947-09-28 | 9999-12-31 |
我们可以看到,在SCD2类型中,维护了FROM_DATE和TO_DATE,代表了起始时间、结束时间,业务时间落在其中时就叫生效时间段。
注意
1.自然键相同的维度值,各行之起止时间跨度,不得有交集,从业务上讲不合逻辑,同时会导致数据翻倍。
2.最新的数据,结束时间写“9999-12-31”即可。
3.若基于HQL开发,起始时间、结束时间可直接使用STRING类型,与STRING、DATE类型变量都可以直接进行比较。
3.2 如何使用SCD2
在使用SCD2类型的维度表时也非常简单,只需在WHERE条件或JOIN条件中使用业务时间去限制起始时间、结束时间即可。
如现在有一分析需求,欲从户口事实表中计算1942年各个城市登记入籍的人数。
户口事实表建表语句如下:
--基于HQL方言
CREATE TABLE FACT_HOUSEHOLD(
HOUSEHOLD_NO STRING COMMENT '户口编码',
ADD_NO STRING COMMENT '地域编码',
HOUSEHOLD_CNT INT COMMENT '在籍人数',
HOUSEHOLD_RECORD_DATE STRING COMMENT '入籍时间'
)
数据示例:
HOUSEHOLD_NO | ADD_NO | HOUSEHOLD_CNT | HOUSEHOLD_RECORD_DAY |
---|---|---|---|
0000000001 | 010 | 2 | 1937-09-27 |
0000000003 | 029 | 3 | 2013-09-01 |
0000000004 | 0533 | 3 | 2010-09-01 |
那么需求指标计算SQL为:
--基于HQL方言
SELECT T2.ADD_NAME AS ADD_NAME,SUM(NVL(T1.HOUSEHOLD_CNT,0)) AS ADD_POPULATIONFROM FACT_HOUSEHOLD T1JOIN DIM_ADD_SCD2 T2ON T1.ADD_NO = T2.ADD_NOAND SUBSTR(T2.TO_DATE,1,4) >= '1942'AND SUBSTR(T2.TO_DATE,1,4) <= '1942'WHERE SUBSTR(HOUSEHOLD_RECORD_DAY,1,4) = '1942';
--一般而言,传参会写占位符基于业务时间生成,此为示例,故写为固定值。
以上便是一个简易的demo,介绍了如何在计算指标时使用SCD2类型维度表。
3.3 如何实现SCD2
仍以3.2节中的DIM_ADD_SCD2为例,要对其进行更新,更新的依据是通过ETL获取的最新地域维度信息表——DIM_ADD_NEW。
其建表语句如下:
--基于HQL方言
CREATE TABLE DIM_ADD_NEW(
ADD_NO STRING COMMENT '地域编码',
ADD_NAME STRING COMMENT '地域名称'
)
设其中数据如下:
ADD_NO | ADD_NAME |
---|---|
010 | 北京 |
002 | A3 |
003 | B1 |
004 | C1 |
我们对DIM_ADD_NEW中的数据分而治之:
情况一,在自然键对应时,DIM_ADD_NEW中与DIM_ADD_SCD2最新一条数据无差异,则不做操作。
情况二,在自然键对应时,DIM_ADD_NEW中与DIM_ADD_SCD2最新一条数据有差异,则将此自然键DIM_ADD_SCD2中最新一条数据结束时间设置为业务时间(或业务时间-1d,视具体业务而定),并插入最新维度数据,起始时间设置为业务时间+1d(或业务时间,视具体业务而定),结束时间设置为’9999-12-31’。
情况三,之于某自然键,DIM_ADD_NEW中存在而DIM_ADD_SCD2中不存在,新增之,起始时间设置为业务时间+1d(或业务时间,视具体业务而定),结束时间设置为’9999-12-31’。
下面我们以此命题进行一个小demo代码的撰写:
3.3.1 获取维度基准
设维表有数据如下:
ADD_NO | ADD_NAME | FROM_DATE | TO_DATE |
---|---|---|---|
010 | 北平 | 1368-09-12 | 1947-09-27 |
010 | 北京 | 1947-09-28 | 9999-12-31 |
002 | A1 | 1234-01-01 | 1949-10-01 |
002 | A2 | 1949-10-02 | 9999-12-31 |
003 | B1 | 1234-01-01 | 9999-12-31 |
获取DIM_ADD_SCD2结束日期为9999-12-31的数据作为基准。
--基于HQL方言
CREATE TABLE DIM_ADD_SCD2_NEWEST AS
SELECT T1.ADD_NO,T1.ADD_NAMEFROM DIM_ADD_SCD2 T1WHERE T1.TO_DATE = '9999-12-31';
处理过后数据如下:
ADD_NO | ADD_NAME |
---|---|
010 | 北京 |
002 | A2 |
003 | B1 |
3.3.2 按情况分治打标
对 3.3 中提到的三种情况,对最新数据进行打标,打标字段命名为FLAG,0对应情况一,1对应情况2,2对应情况三。
建表语句如下:
--基于HQL方言
CREATE TABLE DIM_ADD_NEW_FLAG(
ADD_NO STRING COMMENT '地域编码',
ADD_NAME STRING COMMENT '地域名称',
FLAG STRING COMMENT '情况标识'
)
对应情况一,有代码:
--基于HQL方言
INSERT INTO TABLE DIM_ADD_NEW_FLAG
SELECT T1.ADD_NO AS ADD_NO ,T1.ADD_NAME AS ADD_NAME ,'1' AS FLAGFROM DIM_ADD_NEW T1JOIN DIM_ADD_SCD2_NEWEST T2 --JOIN起过滤作用ON T1.ADD_NO = T2.ADD_NO AND T1.ADD_NAME = T2.ADD_NAME ;
对应情况二,有代码:
--基于HQL方言
INSERT INTO TABLE DIM_ADD_NEW_FLAG
SELECT T1.ADD_NO AS ADD_NO ,T1.ADD_NAME AS ADD_NAME ,'2' AS FLAGFROM DIM_ADD_NEWT1LEFT JOIN DIM_ADD_SCD2_NEWEST T2ON T1.ADD_NO = T2.ADD_NO AND T1.ADD_NAME = T2.ADD_NAME WHERE T2.ADD_NO IS NULL --关联不上,代表有更新数据AND T1.ADD_NO IN (SELECT ADD_NO FROM OLD_NEWEST) --自然键需重合;
对应情况三,有代码:
--基于HQL方言
INSERT INTO TABLE DIM_ADD_NEW_FLAG
SELECT T1.ADD_NO AS ADD_NO ,T1.ADD_NAME AS ADD_NAME ,'3' AS FLAGFROM DIM_ADD_NEW T1LEFT JOIN DIM_ADD_SCD2_NEWEST T2ON T1.ADD_NO = T2.ADD_NO AND T1.ADD_NAME = T2.ADD_NAME WHERE T2.ADD_NO IS NULL --关联不上,代表是新数据AND T1.ADD_NO NOT IN (SELECT ADD_NO FROM OLD_NEWEST) --自然键需不对称;
则此时,DIM_ADD_NEW_FLAG有数据:
ADD_NO | ADD_NAME | FLAG |
---|---|---|
010 | 北京 | 1 |
002 | A3 | 2 |
003 | B1 | 1 |
004 | C1 | 3 |
3.3.3更新维度表
本demo基于HQL方言,故无法进行UPDATE操作,在更新时需使用如下代码:
首先,生成一个临时表,作为处理结果的载体,并以此更新维度表,建表语句与DIM_ADD_SCD2相同:
--基于HQL方言
CREATE TABLE DIM_ADD_SCD2_TMP(
ADD_NO STRING COMMENT '地域编码',
ADD_NAME STRING COMMENT '地域名称',
FROM_DATE STRING COMMENT '起始时间',
TO_DATE STRING COMMENT '结束时间'
)
对于情况1,使用自然键将DIM_ADD_SCD2 与DIM_ADD_NEW_FLAG关联,原样装载至临时表即可 :
--基于HQL方言
INSERT INTO TABLE DIM_ADD_SCD2_TMP
SELECT T1.ADD_NO AS ADD_NO ,T1.ADD_NAME AS ADD_NAME ,T1.FROM_DATE AS FROM_DATE ,T1.TO_DATE AS TO_DATE FROM DIM_ADD_SCD2 T1JOIN DIM_ADD_NEW_FLAG T2ON T1.ADD_NO = T2.ADD_NO AND T2.FLAG = '1';
对于情况二,我们需要将原维度表非最新数据原样装载。最新一条数据的结束时间设为业务时间。并基于DIM_ADD_NEW_FLAG新增一条数据,开始时间为业务时间+1d,结束时间为‘9999-12-31’。
--基于HQL方言
--原样装载非最新数据
INSERT INTO TABLE DIM_ADD_SCD2_TMP
SELECT T1.ADD_NO AS ADD_NO ,T1.ADD_NAME AS ADD_NAME ,T1.FROM_DATE AS FROM_DATE ,T1.TO_DATE AS TO_DATE FROM DIM_ADD_SCD2 T1JOIN DIM_ADD_NEW_FLAG T2ON T1.ADD_NO = T2.ADD_NO AND T2.FLAG = '2'
WHERE T1.TO_DATE != '9999-12-31'UNION ALL--将最新一条数据至结束时间设置为业务时间
SELECT T1.ADD_NO AS ADD_NO ,T1.ADD_NAME AS ADD_NAME ,T1.FROM_DATE AS FROM_DATE ,'${business_tm}' AS TO_DATE --一般而言会传入一个业务时间,此处用占位符表示FROM DIM_ADD_SCD2 T1JOIN DIM_ADD_NEW_FLAG T2ON T1.ADD_NO = T2.ADD_NO AND T2.FLAG = '2'
WHERE T1.TO_DATE = '9999-12-31' UNION ALL--插入最新维度值
SELECT ADD_NO AS ADD_NO ,ADD_NAME AS ADD_NAME ,DATE_ADD('${business_tm}',1) AS FROM_DATE ,'9999-12-31' AS TO_DATEFROM DIM_ADD_NEW_FLAG
WHERE FLAG = '2' ;
对于情况三,将新数据装载至维度表即可,有代码如下:
--基于HQL方言
INSERT INTO TABLE DIM_ADD_SCD2_TMP
SELECT ADD_NO AS ADD_NO ,ADD_NAME AS ADD_NAME ,DATE_ADD('${business_tm}',1) AS FROM_DATE ,'9999-12-31' AS TO_DATEFROM DIM_ADD_NEW_FLAG
WHERE FLAG = '3'
最后,我们将临时表的数据覆写至维度表。
--基于HQL方言
INSERT OVERWRITE TABLE DIM_ADD_SCD2
SELECT ADD_NO AS ADD_NO ,ADD_NAME AS ADD_NAME ,'${business_tm}' AS FROM_DATE ,‘9999-12-31’ AS TO_DATEFROM DIM_ADD_SCD2_TMP
新维表中数据如下:
ADD_NO | ADD_NAME | FROM_DATE | TO_DATE |
---|---|---|---|
010 | 北平 | 1368-09-12 | 1947-09-27 |
010 | 北京 | 1947-09-28 | 9999-12-31 |
002 | A1 | 1234-01-01 | 1949-10-01 |
002 | A2 | 1949-10-02 | 2021-11-20 |
002 | A3 | 2021-11-21 | 9999-12-31 |
003 | B1 | 1234-01-01 | 9999-12-31 |
004 | C1 | 2021-11-20 | 9999-12-31 |
至此,一轮SCD2类型的维表更新完毕。
以上就是本章内容,相信大家已经对SCD2类型是什么,如何使用,如何搭建有了清晰的认识。
下一章讲解蜈蚣事实表的归约优化,敬请期待。
【版权所有,翻版必究。】
吴乙己的数仓指南_5.1维度建模技巧之SCD2类型缓慢变化维搭建相关推荐
- 吴乙己的数仓指南_2维度建模核心思想
目录 0.引言 1.维度建模"四部曲" 1.1选择业务过程 1.2声明粒度 1.3确定维度 1.4确定事实 2.总线矩阵 0.引言 前两章讲了很多宽泛,大而全,代码之外的东西,想必 ...
- 数仓缓慢变化维SCD深度讲解
维度缓慢变化维SCD(Slowly Changing Dimensions)一些维度表的数据不是静态的,而是会随着时间而缓慢地变化(这里的缓慢是相对事实表而言,事实表数据变化的速度比维度表快,如果还不 ...
- 数据中台 第7章 数据体系建设:数仓分层设计、数据建模
数据中台数据体系是在全域原始数据的基础上,进行标准定义及分层建模,数据体系建设最终呈现的结果是一套完整.规范.准确的数据体系,可以方便支撑数据应用. 中台数据体系应具备以下特征: ·覆盖全域数据:数据 ...
- 数仓(二)关系建模和维度建模
上一篇我们了解了OLTP和OLAP数据处理类型.OLTP处理的是关系模型表即实体-关系表ER,而OLAP处理的是维度模型表. 数仓(一)简介数仓,OLTP和OLAP 本篇我们来讨论一下数仓中两种建模的 ...
- ☀️ 数仓建模理论,大数据邻域通用的维度建模技巧【建议收藏学习】
文章目录 前言: 正文: 关系建模 关系建模的特点 维度建模 维度建模的特点 事实表 维度模型的分类 各模型的适用场景 建模阶段具体的划分 1. ODS层 ( 原始数据存储层,直接加载原始日志.数据保 ...
- 数据仓库灵魂30问之数仓有哪几种建模思想?
范式建模 范式建模在实际的应用中有:第一范式(1NF).第二范式(2NF).第三范式(3NF).巴斯-科德范式(BCNF).第四范式(4NF).第五范式(5NF) 在企业范式建模中,一般追求三范式,即 ...
- 数据仓库知识点总结(数仓分层建模、维度建模等)
数据仓库知识点总结 推荐学习<华为数据之道><数据仓库工具箱-维度建模权威指南>两本书. 此文档是数据仓库建模的知识点总结文档,在持续更新中(2021-10-13). 文章目录 ...
- 数仓建设(离线和实时)
文档大纲: 一.数仓基本概念 1. 数据仓库架构 我们在谈数仓之前,为了让大家有直观的认识,先来谈数仓架构,"架构"是什么?这个问题从来就没有一个准确的答案.这里我们引用一段话:在 ...
- 一文读懂数仓建设和数据治理
点击上方 "大数据肌肉猿"关注, 星标一起成长 点击下方链接,进入高质量学习交流群 今日更新| 950个转型案例分享-大数据交流群 本文分为两大节介绍,第一节是数仓建设,第二节是数 ...
最新文章
- hibernate查询之条件查询
- CSS设置图片的重复
- Jerry本地安装SAP Kyma的一些失败尝试
- 适合新手使用的编辑制作管理软件:Substance Alchemist Mac版
- linux ls命令无法执行,更新了个依赖程序,结果悲剧了,连ls命令都不能用,大神帮帮忙!...
- Spring注入静态类型
- 2018 Multi-University Training Contest 7 - GuGuFishtion
- 【2019数学建模】国赛C题:机场出租车优化问题(原创)
- Python爬虫实现英汉互译
- WordPress简约mkBlog博客主题模板v2.1
- 洛谷3678:简单的数学题(画柿子+杜教筛)
- 修改系统默认 alert 弹框样式
- 借助云开发实现小程序朋友圈的发布与展示
- python爬虫学习笔记 3.9 (了解参考:训练Tesseract)
- redies 须知小结
- python股票数据分析_用Python抓取新浪的股票数据
- 坏男人是丈夫的最佳人选
- 固定于计算机主机箱中承载,计算机主机完整
- 关于vue+elementui设置div背景图片填充不生效问题
- 易语言lsp劫持_易语言网截插件修复源码
热门文章
- 【华为OD统一考试B卷 | 200分】宜居星球改造计划(Java JavaScript Python)
- 简述ip地址的abc类如何划分_IP地址如何分类 ?ABC类IP是怎么划分的?
- mac中sublime替换回车键
- identity的作用
- 安卓Android Studio布局文件分类存放,java文件分类存放
- gm工具怎样连接mysql_Gm工具2.0连接不上,求助
- startsWith
- Android5.1 Art Hook 技术分享
- 如何安装STM32CubeMX
- 0018__九针串口公母头定义 RS232串口线常见接法与引脚定义