在有些情况下,为了保持历史的一些状态,需要用拉链表来做,这样做目的在可以保留所有状态的情况下可以节省空间。
拉链表适用于以下几种情况吧
数据量有点大,表中某些字段有变化,但是呢变化的频率也不是很高,业务需求呢又需要统计这种变化状态,每天全量一份呢,有点不太现实,
不仅浪费了存储空间,有时可能业务统计也有点麻烦,这时,拉链表的作用就提现出来了,既节省空间,又满足了需求。
一般在数仓中通过增加begin_date,en_date来表示,如下例,后两列是start_date和end_date.
1 2 3 4 5 6 7 8 9 10 | 1 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-20 1 2016-08-20 2016-08-21 支付 2016-08-21 2016-08-21 1 2016-08-20 2016-08-22 完成 2016-08-22 9999-12-31 2 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-20 2 2016-08-20 2016-08-21 完成 2016-08-21 9999-12-31 3 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-21 3 2016-08-20 2016-08-22 支付 2016-08-22 9999-12-31 4 2016-08-21 2016-08-21 创建 2016-08-21 2016-08-21 4 2016-08-21 2016-08-22 支付 2016-08-22 9999-12-31 5 2016-08-22 2016-08-22 创建 2016-08-22 9999-12-31 |
begin_date表示该条记录的生命周期开始时间,end_date表示该条记录的生命周期结束时间;
end_date = ‘9999-12-31’表示该条记录目前处于有效状态;
如果查询当前所有有效的记录,则select * from order_his where dw_end_date = ‘9999-12-31′
如果查询2016-08-21的历史快照,则select * from order_his where begin_date <= ‘2016-08-21′ and end_date >= ‘2016-08-21’
再简单介绍一下拉链表的更新:
假设以天为维度,以每天的最后一个状态为当天的最终状态。
以一张订单表为例,如下是原始数据,每天的订单状态明细
1 2 3 4 5 6 7 8 9 10 | 1 2016-08-20 2016-08-20 创建 2 2016-08-20 2016-08-20 创建 3 2016-08-20 2016-08-20 创建 1 2016-08-20 2016-08-21 支付 2 2016-08-20 2016-08-21 完成 4 2016-08-21 2016-08-21 创建 1 2016-08-20 2016-08-22 完成 3 2016-08-20 2016-08-22 支付 4 2016-08-21 2016-08-22 支付 5 2016-08-22 2016-08-22 创建 |
根据拉链表我们希望得到的是
1 2 3 4 5 6 7 8 9 10 | 1 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-20 1 2016-08-20 2016-08-21 支付 2016-08-21 2016-08-21 1 2016-08-20 2016-08-22 完成 2016-08-22 9999-12-31 2 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-20 2 2016-08-20 2016-08-21 完成 2016-08-21 9999-12-31 3 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-21 3 2016-08-20 2016-08-22 支付 2016-08-22 9999-12-31 4 2016-08-21 2016-08-21 创建 2016-08-21 2016-08-21 4 2016-08-21 2016-08-22 支付 2016-08-22 9999-12-31 5 2016-08-22 2016-08-22 创建 2016-08-22 9999-12-31 |
可以看出 1,2,3,4每个订单的状态都有,并且也能统计到当前的有效状态。
本例以hive为例,只考虑到实现,与性能无关
首先创建表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | CREATE TABLE orders ( orderid INT , createtime STRING, modifiedtime STRING, status STRING ) row format delimited fields terminated by '\t' CREATE TABLE ods_orders_inc ( orderid INT , createtime STRING, modifiedtime STRING, status STRING ) PARTITIONED BY ( day STRING) row format delimited fields terminated by '\t' CREATE TABLE dw_orders_his ( orderid INT , createtime STRING, modifiedtime STRING, status STRING, dw_start_date STRING, dw_end_date STRING ) row format delimited fields terminated by '\t' ; |
首先全量更新,我们先到2016-08-20为止的数据。
初始化,先把2016-08-20的数据初始化进去
1 2 3 4 | INSERT overwrite TABLE ods_orders_inc PARTITION ( day = '2016-08-20' ) SELECT orderid,createtime,modifiedtime,status FROM orders WHERE createtime < '2016-08-21' and modifiedtime < '2016-08-21' ; |
刷到dw中
1 2 3 4 5 6 | INSERT overwrite TABLE dw_orders_his SELECT orderid,createtime,modifiedtime,status, createtime AS dw_start_date, '9999-12-31' AS dw_end_date FROM ods_orders_inc WHERE day = '2016-08-20' ; |
如下结果
1 2 3 4 5 | select * from dw_orders_his; OK 1 2016-08-20 2016-08-20 创建 2016-08-20 9999-12-31 2 2016-08-20 2016-08-20 创建 2016-08-20 9999-12-31 3 2016-08-20 2016-08-20 创建 2016-08-20 9999-12-31 |
剩余需要进行增量更新
1 2 3 4 5 6 7 8 9 10 | INSERT overwrite TABLE ods_orders_inc PARTITION ( day = '2016-08-21' ) SELECT orderid,createtime,modifiedtime,status FROM orders WHERE (createtime = '2016-08-21' and modifiedtime = '2016-08-21' ) OR modifiedtime = '2016-08-21' ; select * from ods_orders_inc where day = '2016-08-21' ; OK 1 2016-08-20 2016-08-21 支付 2016-08-21 2 2016-08-20 2016-08-21 完成 2016-08-21 4 2016-08-21 2016-08-21 创建 2016-08-21 |
先放到增量表中,然后进行关联到一张临时表中,在插入到新表中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | DROP TABLE IF EXISTS dw_orders_his_tmp; CREATE TABLE dw_orders_his_tmp AS SELECT orderid, createtime, modifiedtime, status, dw_start_date, dw_end_date FROM ( SELECT a.orderid, a.createtime, a.modifiedtime, a.status, a.dw_start_date, CASE WHEN b.orderid IS NOT NULL AND a.dw_end_date > '2016-08-21' THEN '2016-08-21' ELSE a.dw_end_date END AS dw_end_date FROM dw_orders_his a left outer join ( SELECT * FROM ods_orders_inc WHERE day = '2016-08-21' ) b ON (a.orderid = b.orderid) UNION ALL SELECT orderid, createtime, modifiedtime, status, modifiedtime AS dw_start_date, '9999-12-31' AS dw_end_date FROM ods_orders_inc WHERE day = '2016-08-21' ) x ORDER BY orderid,dw_start_date; INSERT overwrite TABLE dw_orders_his SELECT * FROM dw_orders_his_tmp; |
在根据上面步骤把2016-08-22号的数据更新进去,最后结果如下
1 2 3 4 5 6 7 8 9 10 11 12 | select * from dw_orders_his; OK 1 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-20 1 2016-08-20 2016-08-21 支付 2016-08-21 2016-08-21 1 2016-08-20 2016-08-22 完成 2016-08-22 9999-12-31 2 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-20 2 2016-08-20 2016-08-21 完成 2016-08-21 9999-12-31 3 2016-08-20 2016-08-20 创建 2016-08-20 2016-08-21 3 2016-08-20 2016-08-22 支付 2016-08-22 9999-12-31 4 2016-08-21 2016-08-21 创建 2016-08-21 2016-08-21 4 2016-08-21 2016-08-22 支付 2016-08-22 9999-12-31 5 2016-08-22 2016-08-22 创建 2016-08-22 9999-12-31 |
至此,就得到了我们想要的数据。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!