在有些情況下,爲了保持歷史的一些狀態,需要用拉鍊表來做,這樣做目的在可以保留所有狀態的情況下可以節省空間。
拉鍊表適用於以下幾種情況吧
數據量有點大,表中某些字段有變化,但是呢變化的頻率也不是很高,業務需求呢又需要統計這種變化狀態,每天全量一份呢,有點不太現實,
不僅浪費了存儲空間,有時可能業務統計也有點麻煩,這時,拉鍊表的作用就提現出來了,既節省空間,又滿足了需求。
一般在數倉中通過增加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 |
至此,就得到了我們想要的數據。