在有些情況下,為了保持歷史的一些狀態,需要用拉鏈表來做,這樣做目的在可以保留所有狀態的情況下可以節省空間。
拉鏈表適用於以下幾種情況吧
數據量有點大,表中某些字段有變化,但是呢變化的頻率也不是很高,業務需求呢又需要統計這種變化狀態,每天全量一份呢,有點不太現實,
不僅浪費了存儲空間,有時可能業務統計也有點麻煩,這時,拉鏈表的作用就提現出來了,既節省空間,又滿足了需求。
一般在數倉中通過增加begin_date,en_date來表示,如下例,后兩列是start_date和end_date.
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 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 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為例,只考慮到實現,與性能無關
首先創建表
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的數據初始化進去
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中
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';
如下結果
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
剩余需要進行增量更新
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
先放到增量表中,然后進行關聯到一張臨時表中,在插入到新表中
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號的數據更新進去,最后結果如下
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
至此,就得到了我們想要的數據。