HIVE- SCD緩慢變化

SCD緩慢變化維,好比一個用戶維表,用戶屬性會變化,可是不會變化很劇烈,可能一年只會變化一兩次,也不會全部用戶的屬性都會有變化,只有少許的數據發生變化,因此叫緩慢變化維。這種問題就是因爲維度的變化所形成的。apache

解決方式:函數

  • 是否保留歷史數據
  • 保留多久歷史數據
  • 歷史狀態如何與事實表關聯

SCD1 保留最新狀態oop

註冊日期 用戶編號 手機號碼
2019-01-01 0001 111111
2019-01-01 0002 222222
2019-01-01 0003 333333
2019-01-01 0004 444444

 

註冊日期 用戶編號 手機號碼 備註
2019-01-01 0001 111111 111111
2019-01-01 0002 233333 (由22222變成23333)
2019-01-01 0003 333333  
2019-01-01 0004 433333 (由44444變成43333)
2019-01-02 0005 555555 (2019-01-02新增)

缺點:沒有任何歷史狀態,歷史發生的事情沒法追溯,企業中不關心歷史狀態的數據,可使用SCD1spa

SCD2 保留全部歷史狀態設計

 

 

註冊日期 用戶編號 手機號碼
2019-01-01 0001 111111
2019-01-01 0002 222222
2019-01-01 0003 333333
2019-01-01 0004 444444

 

註冊日期 用戶編號 手機號碼 t_start_date t_end_date
2019-01-01 0001 111111 2019-01-01 9999-12-31
2019-01-01 0002 233333 2019-01-01 9999-12-31
2019-01-01 0003 333333 2019-01-01 2019-01-01
2019-01-01 0003 344444 2019-01-02 9999-12-31
2019-01-01 0004 433333 2019-01-01 9999-12-31
2019-01-02 0005 555555 2019-01-01 9999-12-31

出現問題:同一個用戶編號的數據出現屢次,與事實表關聯時,每一個訂單就會被關聯出多條記錄,確定會出錯。代理

解決辦法:加上時間限制條件,訂單生成時間在用戶表有效期內數據才作關聯。code

 

SCD3  只保留了最後一次變化記錄,綜合了SCD1和SCD2blog

註冊日期 用戶編號 手機號碼
2019-01-01 0001 111111
2019-01-01 0002 222222
2019-01-01 0003 333333
2019-01-01 0004 444444

 

註冊日期 用戶編號 手機號碼 先前手機號碼
2019-01-01 0001 133333 111111
2019-01-01 0002 233333 222222
2019-01-01 0003 333333  
2019-01-01 0004 444444  

 

HIVE實現SCD2

若是關注歷史狀態基本上用SCD2,若是不關注歷史狀態就用SCD1,SCD3用得比較少。hadoop

SCD2string

1,代理鍵:HIVE中如何實現自增ID

2,如何設計有效期時間

代理鍵的做用:給下表加一個代理ID,對於一個用戶來講,若是狀態發生3次變化,在這個表裏有3條記錄,分別有一個不一樣的ID。用代理鍵ID解決有效期問題。

除了在維表中有代理ID,在事實表裏也會把用戶ID用代理ID替換。關聯的時候就不會出現數據重複的問題,就不須要根據有效期無能去作統計了。

註冊日期 用戶編號 手機號碼 t_start_date t_end_date
2019-01-01 0001 111111 2019-01-01 9999-12-31
2019-01-01 0002 233333 2019-01-01 9999-12-31
2019-01-01 0003 333333 2019-01-01 2019-01-01
2019-01-01 0003 344444 2019-01-02 9999-12-31
2019-01-01 0004 433333 2019-01-01 9999-12-31
2019-01-02 0005 555555 2019-01-01 9999-12-31

有效期開始時間設計成一個很小的時間,在業務開始以前的時間;

有效期終止時間設計成一個很是大的值,一個固定的值。

Hive中的自增ID

  • 利用row_number()
  • org.apache.hadoop.hive.contrib.udf.UDFRowSequence

利用row_number()

select row_number() over(order by empno), empno from emp;

利用org.apache.hadoop.hive.contrib.udf.UDFRowSequence

hdfs dfs -mkdir /user/hive/lib
hdfs dfs -put ${HIVE_HOME}/lib/hive-contrib-1.2.1.jar  /user/hive/lib/

添加Hive函數

hive>create temporary function row_sequence as 'org.apache.hadoop.hive.contrib.udf.UDFSequence';
hive>select row_sequence(), empno from emp limit 10;

添加Hive永久函數

hive>create function row_sequence as 'org.apache.hadoop.hive.contrib.udf.UDFSequence' using jar 'hdfs:///user/hive/lib/hive-contrib-1.2.1.jar';

 準備數據

1,張三,US,CA  
2,李四,US,CB  
3,王五,CA,BB  
4,趙六,CA,BC  
5,老劉,AA,AA  

建立用戶表

-- 能夠建成分區表 ,使用文本文件存儲格式,由於後面用load加載數據,parquet格式的不支持

drop table if exists ods_user_update;

create table ods_user_update (
    user_id INT,  
    name STRING,  
    cty STRING,  
    st STRING
)
COMMENT '每日用戶更新表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'

創建用戶維度表

-- 創建維度表 ,數據不能從外部文件加載,只能從一個hive表加載
create database test;

use test;

drop table if exists dim_user;

CREATE TABLE dim_user (  
    surr_user_id bigint,
    user_id INT,  
    name STRING,  
    cty STRING,  
    st STRING,  
    version INT,  
    ver_start_date DATE,  
    ver_end_date DATE)
COMMENT '每日維度表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'
STORED AS parquet
;

加載初始數據

-- parquet 的表不支持load 數據加載方式

load data local inpath '/root/test/user.txt' overwrite into table ods_user_update;

用戶維度表加載初始數據

INSERT  INTO dim_user  
SELECT  
    ROW_NUMBER() OVER (ORDER BY ods_user_update.user_id) + t2.sk_max,  
    ods_user_update.*,  
    1,  
    CAST('1900-01-01' AS DATE),  
    CAST('2200-01-01' AS DATE)  
from ods_user_update CROSS JOIN (SELECT COALESCE(MAX(surr_user_id),0) sk_max FROM dim_user) t2; 

更新維度表的數據

SET hivevar:pre_date = DATE_ADD(CURRENT_DATE(),-1);  
SET hivevar:max_date = CAST('2200-01-01' AS DATE);

load data local inpath '/root/test/user_update.txt' overwrite into table ods_user_update;

INSERT OVERWRITE TABLE dim_user
SELECT * FROM
(
SELECT A.surr_user_id,
    A.user_id,A.name,a.cty,a.st,a.version,
    A.ver_start_date,
    CASE 
      WHEN B.user_id IS NOT NULL  and A.ver_end_date = ${hivevar:max_date}  then ${hivevar:pre_date}
      ELSE cast(A.ver_end_date as string)
    END AS ver_end_date
FROM dim_user AS A LEFT JOIN ods_user_update AS B
ON A.user_id = B.user_id
UNION
select ROW_NUMBER() OVER (ORDER BY C.user_id) + D.sk_max, 
    c.user_id,c.name,c.cty,C.st,
    0,
    ${hivevar:pre_date} AS ver_start_date,
    ${hivevar:max_date} AS ver_end_date 
from ods_user_update as C cross join (SELECT COALESCE(MAX(surr_user_id),0) sk_max FROM dim_user)  D
) AS T
;
相關文章
相關標籤/搜索