【拉链表】是针对数据仓库设计中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。
【使用场景】
1.表数据量较大,用全量表会占用很多存储
2.表数据会有修改,用增量表,难以处理重复且修改数据
3.有回溯的需求,需要知道历史某个时间点的全量数据
4.数据有修改,但是频率和量不是很大比如只有百万分之一有修改
【处理理论】
首先拉链表是一个全量表且不是分区表,为了达到前面描述的各种效果,必然需要一个中间表来做中间跳板,这个中间跳板表是一个分区表,数据是增量数据,增量内容包括修改和增加,即常常是create_time or update_time
落在当前天,对于拉链表需要增加两个与原始数据没有关系的两个字段来标识数据开始时间和有效截至时间,在示例中,这两个日期分别为start_date
和 end_date
,拉链表其处理方式主要有以下三种:初始化,每天更新数据,回滚数据。
【初始化和新增数据】
初始化部分,是拉链全量表的开始时间,也奠定了回滚时候能够回滚的最早时间,每天更新逻辑如上图,新增数据会分为两部分,一部分是每天新增的数据,对于当天分区里面有相同变化或者未变化的数据时候,分别修改对应的start_date 和 end_date 即可达到更新数据。
【数据回滚】
对于上面的更新逻辑,我们来考虑如何回滚数据,即回到历史的某个时间点,对于拉链表来说是全量表,所以只有一个回滚即可。回滚策略可以根据回滚时间点和数据生成的start_date 和end_date
在end_date rollback_date 的数据要保留,对于处理end_date ≥ rollback_date ≥ start_date 设置end_date为9999-12-31 ,对于回滚的结果,一般为了保持数据的完整性,可以将回滚的数据放在一个新的拉链临时表中。
1、创建表并导入数据
--创建商家信息表(增量表 分区表) drop table if exists ods.ods_trade_shops; create table ods.ods_trade_shops( `shopid` int COMMENT '商铺ID', `userid` int COMMENT '商铺负责人', `areaid` int COMMENT '区域ID', `shopname` string COMMENT '商铺名称', `shoplevel` int COMMENT '商铺等级', `status` int COMMENT '商铺状态', `createtime` string COMMENT '创建日期', `modifytime` string COMMENT '修改日期' ) COMMENT '商家信息表' PARTITIONED BY (`dt` string) row format delimited fields terminated by ','; -- 创建商家信息维表 drop table if exists dim.dim_trade_shops; create table dim.dim_trade_shops( `shopid` int COMMENT '商铺ID', `userid` int COMMENT '商铺负责人', `areaid` int COMMENT '区域ID', `shopname` string COMMENT '商铺名称', `shoplevel` int COMMENT '商铺等级', `status` int COMMENT '商铺状态', `createtime` string COMMENT '创建日期', `modifytime` string COMMENT '修改日期', `startdate` string COMMENT '生效起始日期', `enddate` string COMMENT '失效结束日期' ) COMMENT '商家信息表';
2、拉链表初始化
INSERT OVERWRITE TABLE dim.dim_trade_shops SELECT shopid, userid, areaid, shopname, shoplevel, status, createtime, modifytime, CASE WHEN modifytime IS NOT NULL THEN substr(modifytime, 0, 10) ELSE substr(createtime, 0, 10) END AS startdate, '9999-12-31' AS enddate FROM ods.ods_trade_shops WHERE dt ='2022-12-30';
3、更新拉链表
INSERT OVERWRITE TABLE dim.dim_trade_shops SELECT shopid, userid, areaid, shopname, shoplevel, status, createtime, modifytime, CASE WHEN modifytime IS NOT NULL THEN substr(modifytime, 0, 10) ELSE substr(createtime, 0, 10) END AS startdate, '9999-12-31' AS enddate FROM ods.ods_trade_shops WHERE dt = '2022-12-31' UNION ALL SELECT b.shopid, b.userid, b.areaid, b.shopname, b.shoplevel, b.status, b.createtime, b.modifytime, b.startdate, CASE WHEN a.shopid IS NOT NULL AND b.enddate ='9999-12-31' THEN date_add('2020-11-21', -1) ELSE b.enddate END AS enddate FROM (SELECT * FROM ods.ods_trade_shops WHERE dt='2022-12-31') a RIGHT JOIN dim.dim_trade_shops b ON a.shopid = b.shopid;
加载拉链表的脚本如下:
source /etc/profile if [ -n "$1" ] then do_date=$1 else do_date=`date -d "-1 day" +%F` fi sql=" INSERT OVERWRITE TABLE dim.dim_trade_shops SELECT shopid, userid, areaid, shopname, shoplevel, status, createtime, modifytime, CASE WHEN modifytime IS NOT NULL THEN substr(modifytime, 0, 10) ELSE substr(createtime, 0, 10) END AS startdate, '9999-12-31' AS enddate FROM ods.ods_trade_shops WHERE dt = '$do_date' UNION ALL SELECT b.shopid, b.userid, b.areaid, b.shopname, b.shoplevel, b.status, b.createtime, b.modifytime, b.startdate, CASE WHEN a.shopid IS NOT NULL AND b.enddate ='9999-12-31' THEN date_add('$do_date', -1) ELSE b.enddate END AS enddate FROM (SELECT * FROM ods.ods_trade_shops WHERE dt='$do_date') a RIGHT JOIN dim.dim_trade_shops b ON a.shopid = b.shopid; " hive -e "$sql"
4、回滚拉链表到某一时间点
DROP TABLE IF EXISTS tmp.shops_tmp; CREATE TABLE IF NOT EXISTS tmp.tmp_shops AS SELECT shopid, userid, areaid, shopname, shoplevel, status, createtime, modifytime, startdate, enddate FROM dim.dim_trade_shops WHERE enddate < '2022-12-31' UNION ALL SELECT shopid, userid, areaid, shopname, shoplevel, status, createtime, modifytime, startdate, '9999-12-31' AS enddate FROM dim.dim_trade_shops WHERE startdate <= '2022-12-31' AND enddate >= '2022-12-31'; INSERT OVERWRITE TABLE dim.dim_trade_shops SELECT * FROM tmp.tmp_shops;