在数据仓库中制作拉链表,可以按照以下步骤进行:
需要注意的是,拉链表在数据仓库中的应用可能因具体需求和场景而有所不同。因此,在实际制作拉链表时,建议根据数据仓库的设计规范、业务需求和数据特点进行调整和优化。同时,也要确保数据的完整性、一致性和准确性。
参考:漫谈数据仓库之拉链表(原理、设计以及在Hive中的实现)_拉链表实现方式_小强签名设计的博客-CSDN博客
拉链表是针对数据仓库设计中表存储数据的方式而定义的,顾名思义,所谓拉链,就是记录历史。记录一个事物从开始,一直到当前状态的所有变化的信息。
我们先看一个示例,这就是一张拉链表,存储的是用户的最基本信息以及每条记录的生命周期。我们可以使用这张表拿到最新的当天的最新数据以及之前的历史数据
在数据仓库的数据模型设计过程中,经常会遇到下面这种表的设计:
那么对于这种表我该如何设计呢?下面有几种方案可选:
拉链表在使用上基本兼顾了我们的需求。
首先它在空间上做了一个取舍,虽说不像方案一那样占用量那么小,但是它每日的增量可能只有方案二的千分之一甚至是万分之一。
其实它能满足方案二所能满足的需求,既能获取最新的数据,也能添加筛选条件也获取历史的数据。
所以我们还是很有必要来使用拉链表的。
我们先看一下在 Mysql 关系型数据库里的 user 表中信息变化。
在 2017-01-01 这一天表中的数据是:
在 2017-01-02 这一天表中的数据是,用户 002 和 004 资料进行了修改,005 是新增用户:
在 2017-01-03 这一天表中的数据是,用户 004 和 005 资料进行了修改,006 是新增用户:
如果在数据仓库中设计成历史拉链表保存该表,则会有下面这样一张表,这是最新一天(即 2017-01-03 )的数据:
说明:
在现在的大数据场景下,大部分的公司都会选择以 Hdfs 和 Hive 为主的数据仓库架构。目前的 Hdfs 版本来讲,其文件系统中的文件是不能做改变的,也就是说 Hive 的表只能进行删除和添加操作,而不能进行 update。基于这个前提,我们来实现拉链表。
还是以上面的用户表为例,我们要实现用户的拉链表。在实现它之前,我们需要先确定一下我们有哪些数据源可以用。
而且我们要确定拉链表的时间粒度,比如说拉链表每天只取一个状态,也就是说如果一天有3个状态变更,我们只取最后一个状态,这种天粒度的表其实已经能解决大部分的问题了。
另外,补充一下每日的用户更新表该怎么获取,据笔者的经验,有3种方式拿到或者间接拿到每日的用户增量,因为它比较重要,所以详细说明:
ods 层的 user 表:
现在我们来看一下我们 ods 层的用户资料切片表的结构:
CREATE EXTERNAL TABLE ods.user (
user_num STRING COMMENT '用户编号',
mobile STRING COMMENT '手机号码',
reg_date STRING COMMENT '注册日期'
COMMENT '用户资料表'
PARTITIONED BY (dt string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/ods/user';
)
ods 层的 user_update 表:
然后我们还需要一张用户每日更新表,前面已经分析过该如果得到这张表,现在我们假设它已经存在。
CREATE EXTERNAL TABLE ods.user_update (
user_num STRING COMMENT '用户编号',
mobile STRING COMMENT '手机号码',
reg_date STRING COMMENT '注册日期'
COMMENT '每日用户资料更新表'
PARTITIONED BY (dt string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/ods/user_update';
)
拉链表:
现在我们创建一张拉链表:
CREATE EXTERNAL TABLE dws.user_his (
user_num STRING COMMENT '用户编号',
mobile STRING COMMENT '手机号码',
reg_date STRING COMMENT '用户编号',
t_start_date ,
t_end_date
COMMENT '用户资料拉链表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/dws/user_his';
)
现在我们假设我们已经已经初始化了 2017-01-01 的日期,然后需要更新 2017-01-02 那一天的数据,我们有了下面的 Sql。
然后把两个日期设置为变量就可以了。
INSERT OVERWRITE TABLE dws.user_his
SELECT * FROM
(
SELECT A.user_num,
A.mobile,
A.reg_date,
A.t_start_time,
CASE
WHEN A.t_end_time = '9999-12-31' AND B.user_num IS NOT NULL THEN '2017-01-01'
ELSE A.t_end_time
END AS t_end_time
FROM dws.user_his AS A
LEFT JOIN ods.user_update AS B
ON A.user_num = B.user_num
UNION
SELECT C.user_num,
C.mobile,
C.reg_date,
'2017-01-02' AS t_start_time,
'9999-12-31' AS t_end_time
FROM ods.user_update AS C
) AS T
操作型数据库的用户表结构:
CREATE EXTERNAL TABLE ods.user (
user_num STRING COMMENT '用户编号',
mobile STRING COMMENT '手机号码',
reg_date STRING COMMENT '注册日期' ,
last_modify_date STRING COMMENT '‘最后修改时间’
COMMENT '用户资料表'
PARTITIONED BY (dt string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/ods/user';
每天增量抽取的用户表结构和抽取条件:
1)表结构和上面的表结构保持一致,我们取表名为 ods.user_update
2)增量抽取条件:select * from ods.user where last_modify_date = '$date'
拉链表:
现在我们创建一张拉链表:
CREATE EXTERNAL TABLE dws.user_his (
user_num STRING COMMENT '用户编号',
mobile STRING COMMENT '手机号码',
reg_date STRING COMMENT '用户编号',
last_modify_date STRING COMMENT '‘最后修改时间’
t_start_date ,
t_end_date
COMMENT '用户资料拉链表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/dws/user_his';
)
实现 sql:
1)
merge into dws.user_his tar
using
(
select user_num,mobile from ods.user_update
) sou on tar.user_num=sou.user_num
and tar.mobile=sou.mobile
and tar.t_start_date < '$date'
and tar.t_end_date > '$date'
when matched then
update set tar.t_end_date='9999-12-31';
按照主键筛选,在 dws.user_his
表中出现过的并且现在为有效数据的,全部更新为闭链数据。
INSERT TABLE dws.user_his
SELECT
C.user_num,
C.mobile,
C.reg_date,
c.last_modify_date
'2017-01-02' AS t_start_time,
'9999-12-31' AS t_end_time
FROM ods.user_update AS C;
比如我们要1月2号的数据,取出来的数据为 select from user where t_start_date <= ‘2017-01-02’ and t_end_date >= ‘2017-01-02’
;
与1月2号数据完全一致。
流水表存放的是一个用户的变更记录,比如在一张流水表中,一天的数据中,会存放一个用户的每条修改记录,但是在拉链表中只有一条记录。
这是拉链表设计时需要注意的一个粒度问题。我们当然也可以设置的粒度更小一些,一般按天就足够。
拉链表当然也会遇到查询性能的问题,比如说我们存放了5年的拉链数据,那么这张表势必会比较大,当查询的时候性能就比较低了,个人认为两个思路来解决:
可以采用备份的方案,保证无误和可行。(保存增量数据,并对 t_dw_orders_his
表每个月备份一次全量数据。如需回滚,最多重跑30天数据即可)
在后面的使用中又有了一些心得,补充进来: