数仓建设里有一个拉链表的概念,有不少的朋友在刚开始玩数仓的时候对这一词很是懵逼。以为拉链是这样的:今天就单独搞一篇文章来揭开这个神秘的面纱(数仓这玩意好多专业术语,完全考验理解力,裂开了)
拉链表是数仓建模中常使用的一种方式,从字面意思上比较难理解,这里从数据的角度介绍一下什么是拉链表,首先你需要知道的是拉链表是要有两个时间字段:即开链时间和闭链时间。
以用户维表为例,如果你们的分析场景中可能会比较关注用户信息的变化,而且你们的用户体量非常大,又不能每次都用全量分区的方式来处理,这样不仅浪费资源,而且算力上也会有很大的浪费。那么欢迎你采取拉链表的方案
在风控场景中,需要对一个用户的多个手机号进行跟踪,并利用图谱结合欺诈规则来拦截黑产。
如下图所示,小明突然换了手机号,但是又需要把以前的手机号给存储下来,那么请问在表结构上怎么设计?数据该怎么存?难道真的像图中那样直接进行追加吗?那你可是真会玩。当然这种问题是属于SCD的范畴,如果你学过SCD,那么就会知道其中一种处理手段就是使用拉链表。也就是采用如下图的方式:
通过这种设计方式可以狠方便的追踪到任意一个时刻的用户信息,同时也可以获取到最新的用户信息:
--获取到用户最新信息
select
user_id,phone
from dim_of_user
where link_start_date<=current_date() and link_end_date>=current_date();
--获取到用户历史某一时刻的信息
select
user_id,phone
from dim_of_user
where link_start_date>='2022-01-19' and link_end_date<='2022-01-20'
从上面的代码中可以发现如果获取最新的信息时,每次都要对两个时间进行限定,这在开发起来无疑是有些麻烦的,而且玩不好的话还有可能把两个时间搞错造成数据错误,那怎么办?这个时候可以变通一下,增加一个当前最新状态的标识就完事了呗,即如下图所示:这样改造之后,每次获取最新信息的时候可以执行如下代码:
select
user_id,
phone
from dim_of_user
where is_current=1
拉链表适用于数据体量大,而且需要追踪字段变更记录(变更频率也不会太高)的场景下。
该方案即可以满足分析历史变更情况,同时也尽可能的节约存储,但其开发逻辑相对比较复杂,维度成本比较高。
一般在数据同步阶段和处理SCD的时候使用,比如用户维表或者订单维表。在前面也介绍了实际的风控的业务场景。
从上述中的数据层面和场景结合介绍了拉链表,那么本小节从工程方面来实操一下拉链表的生成。示例 :2022-01-19进行拉链表初始化,2022-01-20号小明发起了更改手机号的操作,同时新增了小王用户。表格式如下:
--注意:该表是属于增量表,这里作为例子,选择tmp层
create table wedw_tmp.tmp_user_info(
user_id string,
user_name string,
birthday string,
phone string,
gender string
)stored as orc;
--1、初始化
insert into wedw_tmp.tmp_user_info
select "a" as user_id,"小明" as user_name,"1992-01-02" as birthday,"123324324" as phone,"男" as gender
union all
select "b" as user_id,"小李" as user_name,"2000-03-04" as birthday,"242343242" as phone,"男" as gender
--2、拉链表,这里使用分区来保留上一次拉链的结果
create table wedw_dw.user_info_dz(
user_id string,
user_name string,
birthday string,
phone string,
gender string,
link_start_date string,
link_end_date string
)
partitioned by(date_id string)
stored as orc;
--3、初始化拉链表
insert overwrite table wedw_dw.user_info_dz partition(date_id='2022-01-19')
select
user_id,
user_name,
birthday,
phone,
gender,
"2022-01-19" as link_start_date,
"9999-12-31" as link_end_date
from wedw_tmp.tmp_user_info
--4、2022-01-20号小明更改了手机号,同时新增了小王用户,那么在数据同步阶段采取增量的方式,则临时表的数据如下:
insert overwrite table wedw_tmp.tmp_user_info
select "a" as user_id,"小明" as user_name,"1992-01-02" as birthday,"43534535" as phone,"男" as gender
union all
select "c" as user_id,"小王" as user_name,"1995-01-02" as birthday,"3453543534" as phone,"男" as gender
--5、此时拉链表的处理逻辑如下:
insert overwrite table wedw_dw.user_info_dz partition(date_id='2022-01-20')
-- 5.1、先挑选出已经闭链的数据和仍处于开链并未发生变化的数据(即未发生变更的数据)
select
t1.user_id,
t1.user_name,
t1.birthday,
t1.phone,
t1.gender,
t1.link_start_date,
t1.link_end_date
from wedw_dw.user_info_dz t1
left join wedw_tmp.tmp_user_info t2
on t1.user_id = t2.user_id
where t1.date_id='2022-01-19' and (t1.link_end_date<'2022-01-20' or t2.user_id is null)
union all
-- 5.2、对发生变更的数据和新增的数据(增量数据)进行初始化开闭链时间
select
user_id,
user_name,
birthday,
phone,
gender,
'2022-01-20' as link_start_date,
'9999-12-31' as link_end_date
from wedw_tmp.tmp_user_info
union all
--5.3、对拉链表中发生变更的数据,进行闭链操作
select
t1.user_id,
t1.user_name,
t1.birthday,
t1.phone,
t1.gender,
t1.link_start_date,
'2022-01-20' as link_end_date
from wedw_tmp.tmp_user_info t2
join wedw_dw.user_info_dz t1
on t1.user_id = t2.user_id
where t1.date_id='2022-01-19'
如果2022-01-21号又新增了一个小刘用户,同时小李更改了性别,那么经过上面的代码逻辑可以得到如下图的结果:
需要特点注意一点的是:本例子中使用的临时表是属于增量的模式,代码逻辑相对比较简单,如果临时表采用全量的模式那么代码就不能这么写了。
马上过春节了,加上现在疫情比较严重,希望朋友们都能度过一个好年,同时在回家的旅途中注意个人防护。
另外告诉大家一个好消息,后续资料资源的获取,大家可以通过如下网址直接获取:http://www.codingcms.top/
大家无需注册,目前注册条件比较严格,暂不公开。所以外部暴漏出一个公共的入口提供给大家下载资源~
如果你所在的城市因为疫情管控不能返乡,没关系,让我们一起卷起来。如果你有更多关于大数据的问题,欢迎加入我们一起探讨。