DW層更新:HIVE腳本三步實現“緩慢變化維2更新”--保留歷史數據

今天,就分享一下我過去一週的兩點工作收穫:
1、DW層更新:“緩慢變化維2更新”,HIVE腳本三步實現
2、ODS層更新:源數據去重的兩種方式

“緩慢變化維1”是全量覆蓋,一步到位。而"緩慢變化維2",要保留歷史數據,實現需要三步走。
已經好幾個月沒有接觸HIVE了,之前也提到我們的人力項目的HIVE數倉被替換成了oracle數倉。在項目結束之際,“經營駕駛倉”的源浩大佬善意提醒:
“你之前的‘緩慢變換維’,hive腳本,是不是少了一步?”
我之前給大家的科普的思路只有兩步:
1、獲取“新”的有效數據



     UNION ALL

2、獲取“新”的失效數據

  • 源表:ODS. workplace_info
CREATE TABLE IF NOT EXISTS ODS.WORKPLACE_INFO
(
      ID STRING COMMENT '企業員工唯一編碼'
    , NAME STRING COMMENT '姓名'
    , PROVINCE STRING COMMENT '工作地點--省'
    , CITY STRING COMMENT '工作地點--市'
    , LAST_UPDATE_DT STRING COMMENT '更新時間'
)
COMMENT '工作地點信息表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\036'
STORED AS parquet;
  • 維表:DW.dim_workplace_info
CREATE TABLE IF NOT EXISTS DW.DIM_WORKPLACE_INFO
(
      SK INT COMMENT '工作地點維度表代理鍵'
    , ID STRING COMMENT '企業員工唯一編碼'
    , NAME STRING COMMENT '姓名'
    , PROVINCE STRING COMMENT '工作地點--省'
    , CITY STRING COMMENT '工作地點--市'
    , VALID_START_DT STRING COMMENT '當前行生效日期'
    , VALID_END_DT STRING COMMENT '當前行失效日期'
    , ROW_STATUS STRING COMMENT '當前行是否有效'
)
COMMENT '工作地點維度'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\036'
STORED AS parquet;
  • 更新思路:

1、獲取“新”的有效數據:從【源表】獲取相對於【維度表】新增的數據:
ODS. workplace_info ods

【left join】

DW.dim_workplace_info dw

WHERE dw.sk is null
OR ods.province <>dw.province
OR ods.city<>ods.city

    UNION ALL

2、獲取“新”的失效數據:從【維度表】獲取"相對於【源表】有變化的數據":
DW.dim_workplace_info dw

【left join】

ODS. workplace_info ods

只要 dw.province <>ods.province 或 dw.city<>ods.city,該維度表需要做出以下操作:

  1. 行的“有效截至時間”vaild_end_dt的數據值需要更改成當前/當前的前一天
  2. “行狀態”row_status標記爲“失效”
  • 代碼實例:
INSERT OVERWRITE TABLE dw.dim_workplace_info
SELECT
--維度表的代理鍵,由維度表中【最大值的代理鍵】 + 【新增數據排序結果】 得到的
      ROW_NUMBER() OVER ( ORDER BY t1.name, t1.province, t1.city) + max_sk AS sk      
    , t1.id
    , t1.name
    , t1.province
    , t1.city
    , CASE 
        WHEN t1.last_update_dt IS NOT NULL 
            THEN t1.last_update_dt
        ELSE DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd') --當前的前一天
      END AS valid_start_dt
    , '99991231' AS valid_end_dt
    , 'Current' AS row_status
--(一)、獲取“新”的有效數據:步驟1 [left join] 步驟2
FROM
---1、獲取源數據
    ods.workplace_info t1
---2、獲取當前行有效的維度數據    
LEFT OUTER JOIN
(
    SELECT
          sk
        , id
        , name
        , province
        , ctiy
    FROM
        dw.dim_workplace_info
    --獲取當前行有效的維度數據的【限制條件】
    WHERE valid_start_dt <= DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
        AND valid_end_dt> DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
)t2
ON t1.id= t2.id
--獲取維度表最大的外鍵,以便爲新增數據生成外鍵
CROSS JOIN
(
    SELECT
        COALESCE(MAX(sk),0) AS max_sk
    FROM
        dw.dim_workplace_info
)dim_sk_tmp
WHERE t2.sko is null
    OR t1.province <> t2.province
    OR t1.city <> t2.city
UNION ALL
--(二)、獲取“新”的失效數據:步驟3 [left outer join] 步驟4
SELECT
      t3.sk
    , t3.id
    , t3.name
    , t3.province
    , t3.city
    , t3.gp_ent_short_des
    , t3.valid_start_dt
    , CASE
        WHEN t3.province <> t4.province
            OR t3.city <> t4.city
                THEN DATE_FORMAT( DATA_SUB(CURRENT_DATE,2),'yyyyMMdd')
        ELSE t3.valid_end_dt  --將維表SCD2字段有變化的某行的有效截止時間valid_end_dt 修改成當前日期的前天
      END AS valid_end_dt
    , CASE
        WHEN t3.province <> t4.province
            OR t3.city <> t4.city
                THEN 'Expired' --將維表SCD2字段有變化的某行的狀態row_status標記爲“失效”--Expired
        ELSE t3.row_status
      END AS row_status
FROM
--3、獲取維度數據
(
    SELECT
          sk
        , id
        , name
        , province
        , ctiy
    FROM
        dw.dim_workplace_info
    --獲取當前行有效的維度數據的【限制條件】
    WHERE valid_start_dt <= DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
        AND valid_end_dt> DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
) t3
--4、獲取源數據
LEFT OUTER JOIN
    ods_gr_sci.sector_ent t4
ON t3.id = t4.id

以上兩個步驟寫出來的腳本,好像沒什麼毛病,能跑起來,可以滿足需求。但你會發現更新前的“失效數據”不見了。爲什麼?你仔細看上面的兩個步驟,維度表取的都是“當前行有效”的數據,而“當前行失效”的數據不見了。所以以上腳本,每次更新,都會將上次得到的失效(歷史)數據給覆蓋沒了。所以正確的“緩慢變化維2更新”腳本應該增加第三步:
1、獲取“新”的有效數據

     UNION ALL

2、獲取“新”的失效數據

    UNION ALL  

3、獲取“舊”的失效數據

  • 完整代碼實例
INSERT OVERWRITE TABLE dw.dim_workplace_info
SELECT
--維度表的代理鍵,由維度表中【最大值的代理鍵】 + 【新增數據排序結果】 得到的
      ROW_NUMBER() OVER ( ORDER BY t1.name, t1.province, t1.city) + max_sk AS sk      
    , t1.id
    , t1.name
    , t1.province
    , t1.city
    , CASE 
        WHEN t1.last_update_dt IS NOT NULL 
            THEN t1.last_update_dt
        ELSE DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd') --當前的前一天
      END AS valid_start_dt
    , '99991231' AS valid_end_dt
    , 'Current' AS row_status
--(一)、獲取“新”的有效數據:步驟1 [left join] 步驟2
FROM
---1、獲取源數據
    ods.workplace_info t1
---2、獲取當前行有效的維度數據    
LEFT OUTER JOIN
(
    SELECT
          sk
        , id
        , name
        , province
        , ctiy
    FROM
        dw.dim_workplace_info
    --獲取當前行有效的維度數據的【限制條件】
    WHERE valid_start_dt <= DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
        AND valid_end_dt> DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
)t2
ON t1.id= t2.id
--獲取維度表最大的外鍵,以便爲新增數據生成外鍵
CROSS JOIN
(
    SELECT
        COALESCE(MAX(sk),0) AS max_sk
    FROM
        dw.dim_workplace_info
)dim_sk_tmp
WHERE t2.sko is null
    OR t1.province <> t2.province
    OR t1.city <> t2.city
UNION ALL
--(二)、獲取“新”的失效數據:步驟3 [left outer join] 步驟4
SELECT
      t3.sk
    , t3.id
    , t3.name
    , t3.province
    , t3.city
    , t3.gp_ent_short_des
    , t3.valid_start_dt
    , CASE
        WHEN t3.province <> t4.province
            OR t3.city <> t4.city
                THEN DATE_FORMAT( DATA_SUB(CURRENT_DATE,2),'yyyyMMdd')
        ELSE t3.valid_end_dt  --將維表SCD2字段有變化的某行的有效截止時間valid_end_dt 修改成當前日期的前天
      END AS valid_end_dt
    , CASE
        WHEN t3.province <> t4.province
            OR t3.city <> t4.city
                THEN 'Expired' --將維表SCD2字段有變化的某行的狀態row_status標記爲“失效”--Expired
        ELSE t3.row_status
      END AS row_status
FROM
--3、獲取維度數據
(
    SELECT
          sk
        , id
        , name
        , province
        , ctiy
    FROM
        dw.dim_workplace_info
    --獲取當前行有效的維度數據的【限制條件】
    WHERE valid_start_dt <= DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
        AND valid_end_dt> DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
) t3
--4、獲取源數據
LEFT OUTER JOIN
    ods_gr_sci.sector_ent t4
ON t3.id = t4.id
--(三)獲取“舊”的失效數據
UNION ALL
SELECT
     sk
    , id
    , name
    , province
    , ctiy
    , valid_start_dt
    , valid_end_dt
    , row_status
FROM
    dw.dim_workplace_info
    --獲取當前行失效的維度數據的【限制條件】
WHERE  valid_end_dt<= DATE_FORMAT( DATA_SUB(CURRENT_DATE,1),'yyyyMMdd')
  • 影響更新成敗的細節:
    還有一個特別重要的細節,就是ODS過來的數據,要確保當前時間內,一個ID只有一條數據,否則更新到DW時會數據發散。所以這裏就要涉及到“源數據”的去重了:
  1. 兩條數據完全相同的,全部字段GROUP BY 就可以
  • 代碼示例:
SELECT
      id
    , name
    , province
    , ctiy
    , MAX(last_update_dt) AS ast_update_dt
FROM
    ods.dim_workplace_info
GROUP BY           
    id
    , name
    , province
    , ctiy
  1. 兩條數據不完全相同,ROW_NUMNER()
  • 代碼示例:
SELECT
      id
    , name
    , province
    , ctiy
    , last_update_dt
FROM
(
SELECT
      id
    , name
    , province
    , ctiy
    , last_update_dt
    ,ROW_NUMBER()OVER(PARTITION BY id ORDER BY name,province,ctiy) AS NUM
FROM
    ods.workplace_info
) T1
WHERE NUM=1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章