今天,就分享一下我过去一周的两点工作收获:
1、DW层更新:“缓慢变化维2更新”,HIVE脚本三步实现
2、ODS层更新:源数据去重的两种方式
“缓慢变化维1”是全量覆盖,一步到位。而"缓慢变化维2",要保留历史数据,实现需要三步走。
已经好几个月没有接触HIVE了,之前也提到我们的人力项目的HIVE数仓被替换成了oracle数仓。在项目结束之际,“经营驾驶仓”的源浩大佬善意提醒:
“你之前的‘缓慢变换维’,hive脚本,是不是少了一步?”
我之前给大家的科普的思路只有两步:
1、获取“新”的有效数据
UNION ALL
2、获取“新”的失效数据
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;
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,该维度表需要做出以下操作:
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')
SELECT
id
, name
, province
, ctiy
, MAX(last_update_dt) AS ast_update_dt
FROM
ods.dim_workplace_info
GROUP BY
id
, name
, province
, ctiy
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