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 |
若是關注歷史狀態基本上用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()
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 ;