一、拉链表定义
记录每条信息的生命周期,一旦一条记录的生命周期结束,就重新开始一条新的记录,并把当前日期放入生效开始日期。如果当前信息至今有效,在生效结束日其中填入一个极大值(如:9999-99-99)
生效日期 <= 某个日期 且 >=某个日期
二、为什么要做拉链表
拉链表适合于:数据会发生变化,但是大多数的数据是不变的,即缓慢变化维
比如: 订单信息从未支付、已支付、未发货、已完成等状态经历了一周,大部分时间是不变化的,如果数据量规模较大,按照每日全量的方式保存效率很低,如:1亿用户 * 365天,每天一份用户信息(每日全量效率低)。
三、拉链表形成过程
通过生效日期 <= 某个日期 且 >=某个日期,得到某个时间点的全量切片。
1)拉链表数据
2)例如:获取2019-01-01的历史切片:select * from order_info where start_date<='2019-01-01' and end_date>='2019-01-01';
3)例如:获取2019-01-02的历史切片:select * from order_info where start_date<='2019-01-02' and end_date>='2019-01-02';
四、拉链表制作流程图
将首日全部数据作为初始拉链表dwd_order_info_his;第二日,创建临时拉链表和第二日变化数据(新增和变化)制作成一个新的拉链表,以此类推。
五、拉链表制作实战
5.1初始化拉链表
1)建立拉链表
drop table if exists dwd_order_info_his;
create external table dwd_order_info_his(
`id` string COMMENT '订单编号',
`total_amount` decimal(10,2) COMMENT '订单金额',
`order_status` string COMMENT '订单状态',
`user_id` string COMMENT '用户id' ,
`payment_way` string COMMENT '支付方式',
`out_trade_no` string COMMENT '支付流水号',
`create_time` string COMMENT '创建时间',
`operate_time` string COMMENT '操作时间',
`start_date` string COMMENT '有效开始日期',
`end_date` string COMMENT '有效结束日期'
) COMMENT '订单拉链表'
stored as parquet
location '/warehouse/gmall/dwd/dwd_order_info_his/'
tblproperties ("parquet.compression"="snappy");
初始日期以2019-02-13为例。
2)初始化拉链表
从ods层直接将ods_order_info 所有数据拿过来
insert overwrite table dwd_order_info_his
select
id,
total_amount,
order_status,
user_id,
payment_way,
out_trade_no,
create_time,
operate_time,
'2019-02-13',
'9999-99-99'
from ods_order_info oi
where oi.dt='2019-02-13';
5.2 制作当日变动数据(新增和修改),每日执行
(1)如何获取每日变动表
- 表内有创建时间和变动时间,可直接获取
- 表内没有创建时间和变动时间,利用第三方工具监控比如canal,监控MySQL的实时变化进行记录
- 业务数据库提供变动流水
我们dwd_order_info在我们的dwd层是根据每日partition的,所以我们根据日期倒过来的数据就是新增变动明细表。
5.3先合并变动信息,再追加新增信息,插入到临时拉链表中
1)建立临时表
drop table if exists dwd_order_info_his_tmp;
create table dwd_order_info_his_tmp(
`id` string COMMENT '订单编号',
`total_amount` decimal(10,2) COMMENT '订单金额',
`order_status` string COMMENT '订单状态',
`user_id` string COMMENT '用户id' ,
`payment_way` string COMMENT '支付方式',
`out_trade_no` string COMMENT '支付流水号',
`create_time` string COMMENT '创建时间',
`operate_time` string COMMENT '操作时间',
`start_date` string COMMENT '有效开始日期',
`end_date` string COMMENT '有效结束日期'
) COMMENT '订单拉链临时表'
stored as parquet
location '/warehouse/gmall/dwd/dwd_order_info_his_tmp/'
tblproperties ("parquet.compression"="snappy");
2)导入脚本
insert overwrite table dwd_order_info_his_tmp
select * from
(
select
id,
total_amount,
order_status,
user_id,
payment_way,
out_trade_no,
create_time,
operate_time,
'2019-02-14' start_date,
'9999-99-99' end_date
from dwd_order_info where dt='2019-02-14'
union all
select oh.id,
oh.total_amount,
oh.order_status,
oh.user_id,
oh.payment_way,
oh.out_trade_no,
oh.create_time,
oh.operate_time,
oh.start_date,
if(oi.id is null, oh.end_date, date_add(oi.dt,-1)) end_date
from dwd_order_info_his oh left join
(
select
*
from dwd_order_info
where dt='2019-02-14'
) oi
on oh.id=oi.id and oh.end_date='9999-99-99'
)his
order by his.id, start_date;
解析:
订单变化表 union all (初始拉链表dwd_order_info_his left join 订单变化表dwd_order_info)
为什么要加
if(oi.id is null, oh.end_date, date_add(oi.dt,-1)) end_date
oi 订单变化表, oh 初始拉链表
因为订单变化表中id在初始拉链表中不存在,为null,即为新增数据,则这条数据的结束日期就是该条数据的结束日期,不需要改变。
订单变化表中id在初始拉链表中存在,不为null,即为需要变化数据,则这条数据的添加到拉链表中时,要将id相同的上一条数据的日期的9999-99-99改成date_add(oi.dt,-1),即新的这条数据的前一天的日期。
为什么要找出这条9999-99-99的数据?
因为 相同id的数据有多条的时候,会将所有该id的数据的结束日期修改date_add(oi.dt,-1),例如:
id status start_date end_date
1 1 2019-01-10 2019-02-13
1 2 2019-02-13 9999-99-99
如果此时来了一条数据:" 1 3 2019-01-10 2019-02013",我们只能修改“1 2 2019-02-13 9999-99-99”不能修改“1 1 2019-01-10 2019-02-13”
3)把临时表覆盖给拉链表
insert overwrite table dwd_order_info_his
select * from dwd_order_info_his_tmp;