数据仓库中历史拉链表的更新方法

柔情痞子 提交于 2020-03-04 07:48:18

 

使用这种方式即可以记录历史,而且最大程度的节省存储。这里简单介绍一下这种历史拉链表的更新方法。

本文中假设:

  1. 数据仓库中订单历史表的刷新频率为一天,当天更新前一天的增量数据;

  2. 如果一个订单在一天内有多次状态变化,则只会记录最后一个状态的历史;

  3. 订单状态包括三个:创建、支付、完成;

  4. 创建时间和修改时间只取到天,如果源订单表中没有状态修改时间,那么抽取增量就比较麻烦,需要有个机制来确保能抽取到每天的增量数据;

  5. 本文中的表和SQL都使用Hive的HQL语法;

  6. 源系统中订单表结构为:

CREATE TABLE orders (
orderid INT,
createtime STRING,
modifiedtime STRING,
status STRING
) stored AS textfile;

7.在数据仓库的ODS层,有一张订单的增量数据表,按天分区,存放每天的增量数据:

CREATE TABLE t_ods_orders_inc (
orderid INT,
createtime STRING,
modifiedtime STRING,
status STRING
) PARTITIONED BY (day STRING)
stored AS textfile;

8. 在数据仓库的DW层,有一张订单的历史数据拉链表,存放订单的历史状态数据:

CREATE TABLE t_dw_orders_his (
orderid INT,
createtime STRING,
modifiedtime STRING,
status STRING,
dw_start_date STRING,
dw_end_date STRING
) stored AS textfile;

9. 暂未考虑Hive上表的查询性能问题,只实现功能;

华丽的分割线:您可以关注  lxw的大数据田地 ,或者  加入邮件列表 ,随时接收博客更新的通知邮件。 

 

10. 2015-08-21至2015-08-23,每天原系统订单表的数据如下,红色标出的为当天发生变化的订单,即增量数据:

全量初始化 

在数据从源业务系统每天正常抽取和刷新到DW订单历史表之前,需要做一次全量的初始化,就是从源订单表中昨天以前的数据全部抽取到ODW,并刷新到DW。

以上面的数据为例,比如在2015-08-21这天做全量初始化,那么我需要将包括2015-08-20之前的所有的数据都抽取并刷新到DW:

第一步,抽取全量数据到ODS:
INSERT overwrite TABLE t_ods_orders_inc PARTITION (day = ‘2015-08-20′)
SELECT orderid,createtime,modifiedtime,status
FROM orders
WHERE createtime <= ‘2015-08-20′;

第二步,从ODS刷新到DW:
INSERT overwrite TABLE t_dw_orders_his
SELECT orderid,createtime,modifiedtime,status,
createtime AS dw_start_date,
‘9999-12-31′ AS dw_end_date
FROM t_ods_orders_inc
WHERE day = ‘2015-08-20′;

完成后,DW订单历史表中数据如下:

  1. spark-sql> select * from t_dw_orders_his;

  2. 1       2015-08-18      2015-08-18      创建    2015-08-18      9999-12-31      

  3. 2       2015-08-18      2015-08-18      创建    2015-08-18      9999-12-31

  4. 3       2015-08-19      2015-08-21      支付    2015-08-19      9999-12-31

  5. 4       2015-08-19      2015-08-21      完成    2015-08-19      9999-12-31

  6. 5       2015-08-19      2015-08-20      支付    2015-08-19      9999-12-31

  7. 6       2015-08-20      2015-08-20      创建    2015-08-20      9999-12-31

  8. 7       2015-08-20      2015-08-21      支付    2015-08-20      9999-12-31

  9. Time taken: 2.296 seconds, Fetched 7 row(s)

华丽的分割线:您可以关注  lxw的大数据田地 ,或者  加入邮件列表 ,随时接收博客更新的通知邮件。 

 

增量抽取 

每天,从源系统订单表中,将前一天的增量数据抽取到ODS层的增量数据表。
这里的增量需要通过订单表中的创建时间和修改时间来确定:
INSERT overwrite TABLE t_ods_orders_inc PARTITION (day = ‘${day}‘)
SELECT orderid,createtime,modifiedtime,status
FROM orders
WHERE createtime = ‘${day}’ OR modifiedtime = ‘${day}';

注意:在ODS层按天分区的增量表,最好保留一段时间的数据,比如半年,为了防止某一天的数据有问题而回滚重做数据。

增量刷新历史数据 

从2015-08-22开始,需要每天正常刷新前一天(2015-08-21)的增量数据到历史表。

第一步,通过增量抽取,将2015-08-21的数据抽取到ODS:
INSERT overwrite TABLE t_ods_orders_inc PARTITION (day = ‘2015-08-21′)
SELECT orderid,createtime,modifiedtime,status
FROM orders
WHERE createtime = ‘2015-08-21′ OR modifiedtime = ‘2015-08-21′;

ODS增量表中2015-08-21的数据如下:

  1. spark-sql> select * from t_ods_orders_inc where day = '2015-08-21';

  2. 3       2015-08-19      2015-08-21      支付    2015-08-21

  3. 4       2015-08-19      2015-08-21      完成    2015-08-21

  4. 7       2015-08-20      2015-08-21      支付    2015-08-21

  5. 8       2015-08-21      2015-08-21      创建    2015-08-21

  6. Time taken: 0.437 seconds, Fetched 4 row(s)

第二步,通过DW历史数据(数据日期为2015-08-20),和ODS增量数据(2015-08-21),刷新历史表:

先把数据放到一张临时表中:

  1. DROP TABLE IF EXISTS t_dw_orders_his_tmp;

  2. CREATE TABLE t_dw_orders_his_tmp AS

  3. SELECT orderid,

  4. createtime,

  5. modifiedtime,

  6. status,

  7. dw_start_date,

  8. dw_end_date

  9. FROM (

  10.     SELECT a.orderid,

  11.     a.createtime,

  12.     a.modifiedtime,

  13.     a.status,

  14.     a.dw_start_date,

  15.     CASE WHEN b.orderid IS NOT NULL AND a.dw_end_date > '2015-08-21' THEN '2015-08-20' ELSE a.dw_end_date END AS dw_end_date

  16.     FROM t_dw_orders_his a

  17.     left outer join (SELECT * FROM t_ods_orders_inc WHERE day = '2015-08-21') b

  18.     ON (a.orderid = b.orderid) 

  19.     UNION ALL

  20.     SELECT orderid,

  21.     createtime,

  22.     modifiedtime,

  23.     status,

  24.     modifiedtime AS dw_start_date,

  25.     '9999-12-31' AS dw_end_date

  26.     FROM t_ods_orders_inc

  27.     WHERE day = '2015-08-21' 

  28. ) x

  29. ORDER BY orderid,dw_start_date;

其中:
UNION ALL的两个结果集中,第一个是用历史表left outer join 日期为 ${yyy-MM-dd} 的增量,能关联上的,并且dw_end_date > ${yyy-MM-dd},说明状态有变化,则把原来的dw_end_date置为(${yyy-MM-dd} – 1), 关联不上的,说明状态无变化,dw_end_date无变化。
第二个结果集是直接将增量数据插入历史表。

最后把临时表中数据插入历史表:
INSERT overwrite TABLE t_dw_orders_his
SELECT * FROM t_dw_orders_his_tmp;

华丽的分割线:您可以关注  lxw的大数据田地 ,或者  加入邮件列表 ,随时接收博客更新的通知邮件。 

 

刷新完后,历史表中数据如下:

  1. spark-sql> select * from t_dw_orders_his order by orderid,dw_start_date;

  2. 1       2015-08-18      2015-08-18      创建    2015-08-18      9999-12-31

  3. 2       2015-08-18      2015-08-18      创建    2015-08-18      9999-12-31

  4. 3       2015-08-19      2015-08-21      支付    2015-08-19      2015-08-20

  5. 3       2015-08-19      2015-08-21      支付    2015-08-21      9999-12-31

  6. 4       2015-08-19      2015-08-21      完成    2015-08-19      2015-08-20

  7. 4       2015-08-19      2015-08-21      完成    2015-08-21      9999-12-31

  8. 5       2015-08-19      2015-08-20      支付    2015-08-19      9999-12-31

  9. 6       2015-08-20      2015-08-20      创建    2015-08-20      9999-12-31

  10. 7       2015-08-20      2015-08-21      支付    2015-08-20      2015-08-20

  11. 7       2015-08-20      2015-08-21      支付    2015-08-21      9999-12-31

  12. 8       2015-08-21      2015-08-21      创建    2015-08-21      9999-12-31

  13. Time taken: 0.717 seconds, Fetched 11 row(s)

  14.  

由于在2015-08-21做了8月20日以前的数据全量初始化,而订单3、4、7在2015-08-21的增量数据中也存在,因此都有两条记录,但不影响后面的查询。

再看将2015-08-22的增量数据刷新到历史表:

  1. INSERT overwrite TABLE t_ods_orders_inc PARTITION (day = '2015-08-22') 

  2. SELECT orderid,createtime,modifiedtime,status

  3. FROM orders

  4. WHERE createtime = '2015-08-22' OR modifiedtime = '2015-08-22';

  5.  

  6. DROP TABLE IF EXISTS t_dw_orders_his_tmp;

  7. CREATE TABLE t_dw_orders_his_tmp AS

  8. SELECT orderid,

  9. createtime,

  10. modifiedtime,

  11. status,

  12. dw_start_date,

  13. dw_end_date

  14. FROM (

  15.     SELECT a.orderid,

  16.     a.createtime,

  17.     a.modifiedtime,

  18.     a.status,

  19.     a.dw_start_date,

  20.     CASE WHEN b.orderid IS NOT NULL AND a.dw_end_date > '2015-08-22' THEN '2015-08-21' ELSE a.dw_end_date END AS dw_end_date

  21.     FROM t_dw_orders_his a

  22.     left outer join (SELECT * FROM t_ods_orders_inc WHERE day = '2015-08-22') b

  23.     ON (a.orderid = b.orderid) 

  24.     UNION ALL

  25.     SELECT orderid,

  26.     createtime,

  27.     modifiedtime,

  28.     status,

  29.     modifiedtime AS dw_start_date,

  30.     '9999-12-31' AS dw_end_date

  31.     FROM t_ods_orders_inc

  32.     WHERE day = '2015-08-22' 

  33. ) x

  34. ORDER BY orderid,dw_start_date;

  35.  

  36.  

  37. INSERT overwrite TABLE t_dw_orders_his

  38. SELECT * FROM t_dw_orders_his_tmp;

  39.  

刷新完后历史表数据如下:

  1. spark-sql> select * from t_dw_orders_his order by orderid,dw_start_date;

  2. 1       2015-08-18      2015-08-18      创建    2015-08-18      2015-08-21

  3. 1       2015-08-18      2015-08-22      支付    2015-08-22      9999-12-31

  4. 2       2015-08-18      2015-08-18      创建    2015-08-18      2015-08-21

  5. 2       2015-08-18      2015-08-22      完成    2015-08-22      9999-12-31

  6. 3       2015-08-19      2015-08-21      支付    2015-08-19      2015-08-20

  7. 3       2015-08-19      2015-08-21      支付    2015-08-21      9999-12-31

  8. 4       2015-08-19      2015-08-21      完成    2015-08-19      2015-08-20

  9. 4       2015-08-19      2015-08-21      完成    2015-08-21      9999-12-31

  10. 5       2015-08-19      2015-08-20      支付    2015-08-19      9999-12-31

  11. 6       2015-08-20      2015-08-20      创建    2015-08-20      2015-08-21

  12. 6       2015-08-20      2015-08-22      支付    2015-08-22      9999-12-31

  13. 7       2015-08-20      2015-08-21      支付    2015-08-20      2015-08-20

  14. 7       2015-08-20      2015-08-21      支付    2015-08-21      9999-12-31

  15. 8       2015-08-21      2015-08-21      创建    2015-08-21      2015-08-21

  16. 8       2015-08-21      2015-08-22      支付    2015-08-22      9999-12-31

  17. 9       2015-08-22      2015-08-22      创建    2015-08-22      9999-12-31

  18. 10      2015-08-22      2015-08-22      支付    2015-08-22      9999-12-31

  19. Time taken: 0.66 seconds, Fetched 17 row(s)

  20.  

华丽的分割线:您可以关注  lxw的大数据田地 ,或者  加入邮件列表 ,随时接收博客更新的通知邮件。 

 

查看2015-08-21的历史快照数据:

  1. spark-sql> select * from t_dw_orders_his where dw_start_date <= '2015-08-21' and dw_end_date >= '2015-08-21';

  2. 1       2015-08-18      2015-08-18      创建    2015-08-18      2015-08-21

  3. 2       2015-08-18      2015-08-18      创建    2015-08-18      2015-08-21

  4. 3       2015-08-19      2015-08-21      支付    2015-08-21      9999-12-31

  5. 4       2015-08-19      2015-08-21      完成    2015-08-21      9999-12-31

  6. 5       2015-08-19      2015-08-20      支付    2015-08-19      9999-12-31

  7. 6       2015-08-20      2015-08-20      创建    2015-08-20      2015-08-21

  8. 7       2015-08-20      2015-08-21      支付    2015-08-21      9999-12-31

  9. 8       2015-08-21      2015-08-21      创建    2015-08-21      2015-08-21

订单1在2015-08-21的时候还处于创建的状态,在2015-08-22的时候状态变为支付。

再刷新2015-08-23的增量数据:

按照上面的方法刷新完后,历史表数据如下:

  1. spark-sql> select * from t_dw_orders_his order by orderid,dw_start_date;

  2. 1       2015-08-18      2015-08-18      创建    2015-08-18      2015-08-21

  3. 1       2015-08-18      2015-08-22      支付    2015-08-22      2015-08-22

  4. 1       2015-08-18      2015-08-23      完成    2015-08-23      9999-12-31

  5. 2       2015-08-18      2015-08-18      创建    2015-08-18      2015-08-21

  6. 2       2015-08-18      2015-08-22      完成    2015-08-22      9999-12-31

  7. 3       2015-08-19      2015-08-21      支付    2015-08-19      2015-08-20

  8. 3       2015-08-19      2015-08-21      支付    2015-08-21      2015-08-22

  9. 3       2015-08-19      2015-08-23      完成    2015-08-23      9999-12-31

  10. 4       2015-08-19      2015-08-21      完成    2015-08-19      2015-08-20

  11. 4       2015-08-19      2015-08-21      完成    2015-08-21      9999-12-31

  12. 5       2015-08-19      2015-08-20      支付    2015-08-19      2015-08-22

  13. 5       2015-08-19      2015-08-23      完成    2015-08-23      9999-12-31

  14. 6       2015-08-20      2015-08-20      创建    2015-08-20      2015-08-21

  15. 6       2015-08-20      2015-08-22      支付    2015-08-22      9999-12-31

  16. 7       2015-08-20      2015-08-21      支付    2015-08-20      2015-08-20

  17. 7       2015-08-20      2015-08-21      支付    2015-08-21      9999-12-31

  18. 8       2015-08-21      2015-08-21      创建    2015-08-21      2015-08-21

  19. 8       2015-08-21      2015-08-22      支付    2015-08-22      2015-08-22

  20. 8       2015-08-21      2015-08-23      完成    2015-08-23      9999-12-31

  21. 9       2015-08-22      2015-08-22      创建    2015-08-22      9999-12-31

  22. 10      2015-08-22      2015-08-22      支付    2015-08-22      9999-12-31

  23. 11      2015-08-23      2015-08-23      创建    2015-08-23      9999-12-31

  24. 12      2015-08-23      2015-08-23      创建    2015-08-23      9999-12-31

  25. 13      2015-08-23      2015-08-23      支付    2015-08-23      9999-12-31

订单1从20号-23号,状态变化了三次,历史表中有三条记录。

  1. //查看2015-08-22当天的历史快照,可以看出,和上面图中2015-08-22时候订单表中的数据是一样的

  2. spark-sql> select * from t_dw_orders_his where dw_start_date <= '2015-08-22' and dw_end_date >= '2015-08-22';

  3. 1       2015-08-18      2015-08-22      支付    2015-08-22      2015-08-22

  4. 2       2015-08-18      2015-08-22      完成    2015-08-22      9999-12-31

  5. 3       2015-08-19      2015-08-21      支付    2015-08-21      2015-08-22

  6. 4       2015-08-19      2015-08-21      完成    2015-08-21      9999-12-31

  7. 5       2015-08-19      2015-08-20      支付    2015-08-19      2015-08-22

  8. 6       2015-08-20      2015-08-22      支付    2015-08-22      9999-12-31

  9. 7       2015-08-20      2015-08-21      支付    2015-08-21      9999-12-31

  10. 8       2015-08-21      2015-08-22      支付    2015-08-22      2015-08-22

  11. 9       2015-08-22      2015-08-22      创建    2015-08-22      9999-12-31

  12. 10      2015-08-22      2015-08-22      支付    2015-08-22      9999-12-31

  13. Time taken: 0.328 seconds, Fetched 10 row(s)

  14. //查看当前所有订单的最新状态

  15. spark-sql> select * from t_dw_orders_his where dw_end_date = '9999-12-31';                                   

  16. 1       2015-08-18      2015-08-23      完成    2015-08-23      9999-12-31

  17. 2       2015-08-18      2015-08-22      完成    2015-08-22      9999-12-31

  18. 3       2015-08-19      2015-08-23      完成    2015-08-23      9999-12-31

  19. 4       2015-08-19      2015-08-21      完成    2015-08-21      9999-12-31

  20. 5       2015-08-19      2015-08-23      完成    2015-08-23      9999-12-31

  21. 6       2015-08-20      2015-08-22      支付    2015-08-22      9999-12-31

  22. 7       2015-08-20      2015-08-21      支付    2015-08-21      9999-12-31

  23. 8       2015-08-21      2015-08-23      完成    2015-08-23      9999-12-31

  24. 9       2015-08-22      2015-08-22      创建    2015-08-22      9999-12-31

  25. 10      2015-08-22      2015-08-22      支付    2015-08-22      9999-12-31

  26. 11      2015-08-23      2015-08-23      创建    2015-08-23      9999-12-31

  27. 12      2015-08-23      2015-08-23      创建    2015-08-23      9999-12-31

  28. 13      2015-08-23      2015-08-23      支付    2015-08-23      9999-12-31

  29. Time taken: 0.293 seconds, Fetched 13 row(s)

实际业务中,有可能某一天的数据有问题,需要回滚或重做,这点有点麻烦,后续文章再介绍。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!