HIVE- SCD緩慢變化


SCD緩慢變化維,比如一個用戶維表,用戶屬性會變化,但是不會變化很劇烈,可能一年只會變化一兩次,也不會所有用戶的屬性都會有變化,只有少量的數據發生變化,所以叫緩慢變化維。這種問題就是由於維度的變化所造成的。

解決方式:

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

SCD1 保留最新狀態

注冊日期 用戶編號 手機號碼
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新增)

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

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

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

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

 

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

注冊日期 用戶編號 手機號碼
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用得比較少。

SCD2

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
;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM