1 緩慢變化維
說到歷史拉鏈表剑肯,首先得說下緩慢變化維捧毛。
在現(xiàn)實(shí)世界中观堂,維度的屬性并不是靜態(tài)的,而是隨著時(shí)間的變化而變化呀忧,這也體現(xiàn)了數(shù)據(jù)倉(cāng)庫(kù)的特點(diǎn)之一师痕,是反映歷史變化的。相對(duì)于數(shù)據(jù)增長(zhǎng)較為快速的事實(shí)表而账,維度的變化是相對(duì)緩慢的胰坟。
在維度建模理論中,處理緩慢變化維有三種方式:
新的維度屬性直接覆蓋舊的維度屬性泞辐,不保留歷史數(shù)據(jù)笔横;
增加新的維度行(需要生成代理鍵來支持),維度變化前的事實(shí)關(guān)聯(lián)變化前的維度值咐吼,維度變化后的事實(shí)關(guān)聯(lián)變化后的維度值吹缔。缺點(diǎn)是無法歸一為變化前的維度值或者變化后的維度值進(jìn)行統(tǒng)計(jì);
增加維度列锯茄,即針對(duì)維度的某一屬性時(shí)厢塘,在設(shè)計(jì)表時(shí)需要至少包含兩列,新屬性和舊屬性肌幽。優(yōu)點(diǎn)是可以根據(jù)業(yè)務(wù)需求進(jìn)行不同的歸一化處理晚碾,缺點(diǎn)是擴(kuò)展性不好,保留的維度歷史數(shù)據(jù)有限喂急。
2 歷史拉鏈表
而歷史拉鏈存儲(chǔ)恰恰是對(duì)第二種方式的一種升級(jí)格嘁,同樣是以增加新的維度行來實(shí)現(xiàn),不同的是使用時(shí)間鍵來代替代理鍵廊移。時(shí)間鍵包含兩個(gè)字段糕簿,開始時(shí)間和結(jié)束時(shí)間,一般以天為粒度保留變更的維度數(shù)據(jù)画机。
2.1 查詢方式
查詢當(dāng)前最新狀態(tài)維度數(shù)據(jù):select * from table_name where end_day = ‘30001231’
查詢某一天的維度狀態(tài)數(shù)據(jù):select * from table_name where start_day <= ‘20200201’ and end_day > '20200201'
2.2 加工方式
假設(shè)商品歷史拉鏈表(goods_hist)有如下5個(gè)字段:goods_id(商品編號(hào))冶伞、price(商品價(jià)格)、is_on_sale(商品是否在售)步氏、start_day(開始日期)响禽、end_day(結(jié)束日期)
商品最新全量快照表(goods_cur)有如下3個(gè)字段:goods_id(商品編號(hào))、price(商品價(jià)格)、is_on_sale(商品是否在售)芋类,快照日期為20200201
則SQL加工語(yǔ)句為:
WITH hist AS
(
SELECT goods_id,
price,
is_on_sale,
start_day
FROM goods_hist
WHERE end_day = 30001231
),
cur AS
(
SELECT nvl(goods_id,-1) AS goods_id,
nvl(price,-1) AS price,
nvl(is_on_sale,-1) AS is_on_sale
FROM goods_cur
)
SELECT nvl(cur.goods_id,hist.goods_id) AS goods_id,
nvl(cur.price,hist.price) AS price,
nvl(cur.is_on_sale,hist.is_on_sale) AS is_on_sale,
nvl(hist.start_day,20200201) AS start_day,
CASE
WHEN cur.goods_id IS NULL THEN 20200201
ELSE 30001231
END AS end_day
FROM cur
FULL OUTER JOIN hist
ON cur.goods_id = hist.goods_id
AND cur.price = hist.price
AND cur.is_on_sale = hist.is_on_sale
SQL語(yǔ)句輸出的結(jié)果包括兩部分:
end_day=30001231的最新狀態(tài)維度數(shù)據(jù)
end_day=20200201的已失效的維度數(shù)據(jù)
2.3 分區(qū)方式
方式1(使用start_day作為分區(qū)鍵):缺點(diǎn)是查詢最新數(shù)據(jù)無法走分區(qū)隆嗅;查詢某一天數(shù)據(jù)時(shí)end_day限制條件無法走分區(qū);加工歷史拉鏈表數(shù)據(jù)時(shí)侯繁,end_day=30001231的結(jié)果數(shù)據(jù)不方便入庫(kù)
方式2(使用end_day作為分區(qū)鍵):缺點(diǎn)是查詢某一天數(shù)據(jù)時(shí)start_day限制條件無法走分區(qū)胖喳;優(yōu)點(diǎn)是加工歷史拉鏈表數(shù)據(jù)時(shí),結(jié)果數(shù)據(jù)入庫(kù)方便贮竟,直接insert overwrite覆蓋分區(qū)30001231和20200201即可
方式3(使用start_day和end_day作為聯(lián)合分區(qū)鍵丽焊,start_day為父分區(qū)):查詢最新數(shù)據(jù)時(shí)需要改變下SQL語(yǔ)句,不然無法走分區(qū)(比如當(dāng)前日期是20200401咕别,SQL語(yǔ)句需改為select * from table_name where start_day <= ‘20200401’ and end_day > '20200401'技健,即查詢某一天數(shù)據(jù)的寫法);缺點(diǎn)是加工歷史拉鏈表數(shù)據(jù)時(shí)惰拱,end_day=30001231和end_day=20200201的結(jié)果數(shù)據(jù)都不方便入庫(kù)雌贱;而且分區(qū)數(shù)會(huì)越來越多,一年下來最多可能產(chǎn)生365*364/2=66430個(gè)分區(qū)偿短;優(yōu)點(diǎn)是查詢數(shù)據(jù)時(shí)start_day和end_day的限制條件都可以走分區(qū)
方式4(使用start_day和end_day作為聯(lián)合分區(qū)鍵欣孤,end_day為父分區(qū)):缺點(diǎn)同方式3,但加工歷史拉鏈表數(shù)據(jù)時(shí)昔逗,結(jié)果數(shù)據(jù)入庫(kù)相對(duì)方便(首先將結(jié)果數(shù)據(jù)存入臨時(shí)表降传,然后清空拉鏈表的分區(qū)end_day=30001231和end_day=20200201,最后將臨時(shí)表數(shù)據(jù)以insert into方式入庫(kù))纤子;優(yōu)點(diǎn)同方式3
綜上所述搬瑰,分區(qū)方式可在2和4中選擇。
選擇方式2控硼,需要考慮隨著時(shí)間的推移泽论,查詢某一天的維度狀態(tài)數(shù)據(jù),消耗的計(jì)算資源會(huì)越來越多卡乾∫磴玻可考慮刪除或者備份部分歷史數(shù)據(jù)至其他地方。
選擇方式4幔妨,需要考慮隨著時(shí)間的推移鹦赎,分區(qū)數(shù)量會(huì)越來越多∥蟊ぃ可考慮定期重構(gòu)歷史拉鏈表古话,比如在每個(gè)月月初強(qiáng)制重新開始做歷史拉鏈表(比如在20200401時(shí),先將end_day=30001231的數(shù)據(jù)修改為end_day=20200401锁施,再基于最新全量快照表生成一份start_day=20200401陪踩,end_day=30001231的數(shù)據(jù))杖们。
2.4 注意點(diǎn)
設(shè)計(jì)歷史拉鏈表時(shí),需要移除變化頻率高的維度屬性肩狂,不然生成新拉鏈的概率會(huì)很高摘完,導(dǎo)致無法達(dá)到節(jié)省存儲(chǔ)的目的。