数据仓库 - 拉链表开发实践

一、什么是拉链表

拉链表是针对数据仓库设计中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。

二、拉链表开发

案例:客户数据拉链表

2x01 表设计

存储介质
T_CUSTOMERMySQL
ODS_T_CUSTOMERHIVE
TMP_CUSTOMERMPP
DIM_CUSTOMERMPP

操作系统数据表:T_CUSTOMER

字段类型备注
cidint主键
namevarchar 
phone_numbervarchar 
create_datedate 
update_datedate 

 

ODS数据表:ODS_T_CUSTOMER (全量记录操作系统数据表的历史快照)

字段类型备注
cidint 
namevarchar 
phone_numbervarchar 
create_datedate 
update_datedate 
sync_datedate同步日期

临时数据表:TMP_CUSTOMER

** 根据ODS_T_CUSTOMER的sync_date字段抽取全量最新数据(包括新增,修改的数据)

字段类型备注
cidint主键
namevarchar 
phone_numbervarchar 
create_datedate 
update_datedate 

数仓维度表:DIM_CUSTOMER

字段类型备注
sidint代理键
cidint源表主键
namevarchar 
phone_numbervarchar 
create_datedate 
update_datedate 
valid_fromdate有效期起始日
valid_todate有效期结束日

2x02 流程

1. 利用数据同步工具,例如Sqoop,把操作系统数据表全量数据同步到ODS数据表。

2. 利用Spark抽取当天同步的数据到临时表TMP_CUSTOMER中。

3. 与维度表DIM_CUSTOMER进行JOIN操作,获取变化的记录,并存储。

2x03 实现

1. 同步操作忽略。。。

2. 抽取当天同步数据到临时表忽略。。。

3. 拉链表计算

2020-05-01 操作系统数据

cidnamephone_numbercreate_dateupdate_date
1A123456782020-05-012020-05-01
2B222222222020-05-012020-05-01
3C333333332020-05-012020-05-01

 

2020-05-02 操作系统数据

cidnamephone_numbercreate_dateupdate_date备注
1A123456782020-05-012020-05-01 
2B888888882020-05-012020-05-02修改
3C333333332020-05-012020-05-01 
4D555555552020-05-022020-05-02新增

2020-05-03 操作系统数据 

cidnamephone_numbercreate_dateupdate_date备注
1A123456782020-05-012020-05-01 
2B888888882020-05-012020-05-02 
3C333333332020-05-012020-05-01删除
4D777777772020-05-022020-05-03修改

更新记录: 

-- 新纪录, 通过left join 寻找dim_customer表中为空的记录
-- 要么新增,要么数据已发生变化
WITH new_customer AS (
	SELECT
		t.cid,
		t. NAME,
		t.phone_number,
		t.create_date,
		t.update_date,
		to_char(CURRENT_TIMESTAMP  + '-1 day', 'yyyy-MM-dd') :: DATE AS valid_from,
		'2999-12-31' :: DATE AS valid_to
	FROM
		tmp_customer t
	LEFT JOIN dim_customer d ON t.cid = d.cid
	AND t.create_date = d.create_date
	AND t.update_date = d.update_date
	AND d.valid_to = '2999-12-31'
	WHERE
		d.cid IS NULL
),
 -- 更新维度表中记录,valid_to = 当前日期
update_new_customer (sid) AS (
	UPDATE dim_customer
	SET valid_to = to_char(CURRENT_TIMESTAMP  + '-1 day', 'yyyy-MM-dd') :: Date
	FROM
		new_customer
	WHERE
		dim_customer.cid = new_customer.cid
	AND dim_customer.valid_to = '2999-12-31' 
	RETURNING dim_customer.sid
),
-- 物理删除的记录
delete_customer AS (
	SELECT
		d.sid
	FROM
		dim_customer d
	LEFT JOIN tmp_customer t ON d.cid = t.cid AND d.valid_to = '2999-12-31'
	WHERE
		t.cid IS NULL
), 
 -- 更新维度表中记录,valid_to = 当前日期
update_delete_customer (sid) AS (
	UPDATE dim_customer
	SET valid_to = to_char(CURRENT_TIMESTAMP  + '-1 day', 'yyyy-MM-dd'):: Date
	FROM
		delete_customer
	WHERE
		dim_customer.sid = delete_customer.sid
	AND dim_customer.valid_to = '2999-12-31' 
	RETURNING dim_customer.sid
)
SELECT COUNT(1) FROM new_customer;

插入新记录: 

WITH new_customer AS (
	SELECT
		t.cid,
		t. NAME,
		t.phone_number,
		t.create_date,
		t.update_date,
		to_char(CURRENT_TIMESTAMP  + '-1 day', 'yyyy-MM-dd') :: DATE AS valid_from,
		'2999-12-31' :: DATE AS valid_to
	FROM
		tmp_customer t
	LEFT JOIN dim_customer d ON t.cid = d.cid
	AND t.create_date = d.create_date
	AND t.update_date = d.update_date
	AND d.valid_to = '2999-12-31'
	WHERE
		d.cid IS NULL
),
-- 插入新记录
INSERT INTO dim_customer (
	cid,
	NAME,
	phone_number,
	create_date,
	update_date,
	valid_from,
	valid_to
) SELECT
	cid,
	NAME,
	phone_number,
	create_date,
	update_date,
	valid_from,
	valid_to
FROM
	new_customer;

 

 根据以上逻辑:

2020-05-02 维表数据

sidcidnamephone_numbercreate_dateupdate_datevalid_fromvalid_to备注
11A123456782020-05-012020-05-012020-05-012999-12-31新增
22B222222222020-05-012020-05-012020-05-012999-12-31新增
33C333333332020-05-012020-05-012020-05-012999-12-31新增

2020-05-03 维表数据

sidcidnamephone_numbercreate_dateupdate_datevalid_fromvalid_to备注
11A123456782020-05-012020-05-012020-05-012999-12-31不变
22B222222222020-05-012020-05-012020-05-012020-05-02修改(源数据变化,修改)
33C333333332020-05-012020-05-012020-05-012999-12-31不变
42B888888882020-05-012020-05-022020-05-022999-12-31新增 (源数据变化,新增)
54D555555552020-05-022020-05-022020-05-022999-12-31新增

2020-05-04 维表数据

sidcidnamephone_numbercreate_dateupdate_datevalid_fromvalid_to备注
11A123456782020-05-012020-05-012020-05-012999-12-31不变
22B222222222020-05-012020-05-012020-05-012020-05-02不变
33C333333332020-05-012020-05-012020-05-012020-05-03修改 (源数据删除)
42B888888882020-05-012020-05-022020-05-022999-12-31不变
54D555555552020-05-022020-05-022020-05-022020-05-03修改(源数据变化,修改)
64D777777772020-05-022020-05-032020-05-032999-12-31新增 (源数据变化,新增)

** 为何要分两个脚本?开发过程中发现一个奇怪的问题合并在一个脚本执行,valid_to字段的值不正确,CTE中的update语句在insert语句后执行。这个问题还没找到原因。

解答:根据官方文档说明,with statement 和 主 statement都是同步执行的,它们看到的底层快照其实是一样的。当使用with执行数据更新操作时,需要加上RETURNING语句,作为子查询间的联系。在原生的postgresql成功,但是在MPP数据库中没有获得期望结果。还得继续定位。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值