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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章