【hive】hive仓库之拉链表实现

本文介绍了在数据仓库建设中如何利用拉链表解决用户历史状态查询问题。通过创建全量同步表和历史拉链表,结合分区策略,实现了数据的高效存储和查询。当数据出现问题时,可以从特定日期重新跑数据,保持拉链表的正确性。方案支持物理删除记录的追踪,同时优化了存储和查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在数仓建设中,经常会遇到需要查找某条数据的历史状态及状态改变的时间点,比如查找某个用户历史所有的变更记录,在业务数据库中是会有变更和物理删除,而用户id是主键,所以只会记录用户最新的记录,如果只是全量同步业务数据库中的用户表,在最新分区中无法查到用户变更记录,如果把全量数据每天快照全部保留,会浪费很多空间,并且查询效率低,逻辑也复杂。做成拉链表既能节省空间,也能快速查询出某个用户所有变更记录和变更类型、变更日期。

此方案比我以前博客的方案优化点:数据仓库之拉链表的更新方法
此方案支持数据重跑,即某天数据发现有问题,ods表重新接入数据了,只需要从那天开始重新跑到拉链表中即可,因为这次拉链表设计成了分区表

以下为实现方法:

  1. 先在ods创建和业务数据库同样的表,每天全量同步表数据
drop table if exists flowtest.tmp_user_test_df;
create table if not exists flowtest.tmp_user_test_df (
      user_id          string comment '用户id',
      user_name        string comment '用户名称',
      age              bigint comment '用户年龄',
      gender           string comment '性别'
) comment '用户信息表'              
partitioned by ( pt_day string comment '分区时间-天' )  
--lifecycle 5  --dataWorks和数栈中有此关键字,用于设置表的生命周期
;
  1. 在ods创建历史全量拉链表
drop table if exists flowtest.tmp_user_std_test_df;
create table if not exists flowtest.tmp_user_std_test_df (
      user_id          string comment '用户id',
      user_name        string comment '用户名称',
      age              bigint comment '用户年龄',
      gender           string comment '性别',
      md5_key          string comment '所有字段md5值',
      create_date      string comment '创建日期',
      operation_date   string comment '操作日期',
      operation_type   string comment '操作类型(A:第一次全量,U:更新,I:新增插入,D:物理删除)'
) comment '用户信息拉链表'              
partitioned by ( pt_day string comment '分区时间-天' )  
--lifecycle 5  --dataWorks和数栈中有此关键字,用于设置表的生命周期
;
  1. 插入模拟数据

insert into table flowtest.tmp_user_test_df partition (pt_day = '20201212') values
("00001", "00001", 26, "男"), 
("00002", "00002", 27, "男"), 
("00003", "00003", 21, "女"), 
("00004", "00004", 22, "女"), 
("00005", "00005", 26, "女"), 
("00006", "00006", 26, "男"), 
("00007", "00007", 26, "男"), 
("00008", "00008", 26, "男"),
("00009", "00009", 26, "男"),
("00010", "00010", 26, "男"),
("00011", "00011", 26, "男"),
("00012", "00012", 26, "男");

--删除用户00006,新增00013,修改00011
insert into table flowtest.tmp_user_test_df partition (pt_day = '20201213') values
("00001", "00001", 26, "男"), 
("00002", "00002", 27, "男"), 
("00003", "00003", 21, "女"), 
("00004", "00004", 22, "女"), 
("00005", "00005", 26, "女"), 
("00006", "浮云", 26, "男"), 
("00007", "00007", 26, "男"), 
("00008", "00008", 26, "男"),
("00009", "00009", 26, "男"),
("00010", "00010", 26, "男"),
("00011", "00011", 26, "男"),
("00013", "00013", 26, "男");

--删除用户00011,新增00014,00012,修改00001
alter table flowtest.tmp_user_test_df drop partition (pt_day = '20201214');
insert into table flowtest.tmp_user_test_df partition (pt_day = '20201214') values
("00001", "ganling", 26, "男"), 
("00002", "00002", 27, "男"), 
("00003", "00003", 21, "女"), 
("00004", "00004", 22, "女"), 
("00005", "00005", 26, "女"), 
("00006", "浮云", 26, "男"), 
("00007", "00007", 26, "男"), 
("00008", "00008", 26, "男"),
("00009", "00009", 26, "男"),
("00010", "00010", 26, "男"),
("00013", "00013", 26, "男"),
("00014", "00014", 27, "男"),
("00012", "insert", 27, "男")
;
  1. 向全量拉链表中导入20201212的全量数据
--第一次全量初始化
insert overwrite table flowtest.tmp_user_std_test_df partition (pt_day = '20201212')
select user_id          --用户id
      ,user_name        --用户名称
      ,age              --用户年龄
      ,gender           --性别
      ,md5(concat(nvl(user_id, '')
                 ,nvl(user_name, '')
                 ,nvl(age, '')
                 ,nvl(gender, ''))) as md5_key
      ,'20201212' as create_date      --创建日期
      ,'99991231' as operation_date   --操作日期
      ,'A' as operation_type   --操作类型(A:第一次全量,U:更新,I:新增插入,D:物理删除)
  from flowtest.tmp_user_test_df
 where pt_day = '20201212'
;
  1. 后续每天全量拉链将数据导入拉链表当天分区
--后续全量拉链
insert overwrite table flowtest.tmp_user_std_test_df partition (pt_day = '${today}')
select coalesce(t1.user_id, t2.user_id) as user_id          --用户id
      ,coalesce(t1.user_name, t2.user_name) as user_name        --用户名称
      ,coalesce(t1.age, t2.age) as age              --用户年龄
      ,coalesce(t1.gender, t2.gender) as gender           --性别
      ,coalesce(t1.md5_key, t2.md5_key) as md5_key           --md5值
      --如果t2表的主键user_id为空,创建日期取跑数据当天,其他取t2.create_date
      ,case when t2.user_id is null then '${today}' --跑数当天${bdp.system.bizdate}
       else t2.create_date end as create_date      --创建日期
      --如果t1.user_id为空,则为if(t2.operation_date = '99991231', '${bdp.system.bizdate}', t2.operation_date)('D'),如果t2.user_id为空,则为'99991231'('I'),如果t1.md5_key<>t2.md5_key,则为t1.pt_day('U'),其他为t2.operation_date
      ,case when t1.user_id is null then if(t2.operation_date = '99991231', '${today}', t2.operation_date) --跑数当天${bdp.system.bizdate}
            when t2.user_id is null then '99991231'
            when t1.md5_key <> t2.md5_key then '${today}' --跑数当天${bdp.system.bizdate}
       else t2.operation_date end as operation_date   --操作日期
      --如果t1.user_id为空,则为'D',如果t2.user_id为空,则为'I',如果t1.md5_key<>t2.md5_key,则为'U',其他为t2.operation_type
      ,case when t1.user_id is null then 'D'
            when t2.user_id is null then 'I'
            when t1.md5_key <> t2.md5_key then 'U'
       else t2.operation_type end as operation_type   --操作类型(A:第一次全量,U:更新,I:新增插入,D:物理删除)
  from 
  ( --新的分区全量数据
    select user_id          --用户id
          ,user_name        --用户名称
          ,age              --用户年龄
          ,gender           --性别
          ,md5(concat(nvl(user_id, '')
                         ,nvl(user_name, '')
                         ,nvl(age, '')
                         ,nvl(gender, ''))) as md5_key
      from flowtest.tmp_user_test_df
     where pt_day = '${today}'
  ) t1 
  full outer join 
  ( --老的分区全量数据
    select user_id          --用户id
          ,user_name        --用户名称
          ,age              --用户年龄
          ,gender           --性别
          ,md5_key          --md5值
          ,create_date      --创建日期
          ,operation_date   --操作日期
          ,operation_type   --操作类型(A:第一次全量,U:更新,I:新增插入,D:物理删除)
      from flowtest.tmp_user_std_test_df
     where pt_day = '${yesterday}'
  ) t2 on t1.user_id = t2.user_id 
      --因为存在物理删除,并且物理删除后可能又将这个user_id重新录入,所以关联时过滤掉物理删除记录,直接将以前物理删除记录原样保留即可
      and t2.operation_type <> 'D'
;
Hive是一个开源的数据仓库和查询工具,用于将大数据处理和分析集成在Hadoop生态系统中。拉链是一种在Hive实现数据处理技术,主要用于处理维度数据的历史变化。 拉链实现思路是将每个维度根据指定的生效日期和失效日期进行拆分,生成多个对应不同时间段的维度数据,以保留维度的历史变化记录。在Hive中,可以通过以下步骤来实现拉链: 1. 创建维度和事实:首先,创建维度和事实Hive。维度用于存储维度字段的详细信息,例如员工、产品等;事实用于存储与维度关联的度量数据,例如销售事实。 2. 设计拉链结构:在维度中添加生效日期(start_date)和失效日期(end_date)字段,用于标识每条记录的有效时间段。通常,失效日期为空或未来日期示当前有效数据。 3. 插入初始数据:将初始数据插入维度,即没有历史记录的部分。在start_date字段中填写最早的日期,end_date字段中填写NULL或未来日期。 4. 插入新数据:当维度中的记录有更新或新增时,需要按照拉链的原则进行插入。具体操作是将原有的生效日期字段(start_date)的end_date字段更新为当前日期,并将新数据插入到维度中。 5. 查询数据:在查询维度和事实时,可以通过使用日期条件和JOIN操作,将最近生效的维度数据关联到事实数据上,以获得正确的历史维度信息。 拉链实现使得Hive可以处理维度数据的历史变化情况,并提供了便捷的方式来查询和分析历史数据。它对于构建具有时间依赖性的报和分析非常有用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值