Hive拉链表设计、实现、总结

2024-02-16 20:20
文章标签 实现 设计 总结 hive 拉链

本文主要是介绍Hive拉链表设计、实现、总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

水善利万物而不争,处众人之所恶,故几于道💦

文章目录

      • 环境介绍
      • 实现
        • 1. 初始化拉链表
        • 2. 后续拉链表数据的更新
      • 总结
          • 彩蛋 - 想清空表的数据:转成内部表,清空数据后,再转成外部表,将分区目录删掉,然后再次跑脚本,其他表都没问题就拉链表新算出过期分区的数据拉不进去,这是啥原因?有高人指点一下吗?

环境介绍

  拉链表可以用来记录数据的声明周期,适合那种数据量大但新增和修改频率不是很高的场景。比如总共100万条数据,每天新增大约1万条,修改1万条,这种变化不是很大的维度数据可以用拉链表来存。

  我们这里将拉链表中每日最新的数据放入到9999-12-31分区中,过期的数据放入到前一天的分区中。

  比如,2024-01-12日所有新增和修改数据(该拉链表采用增量同步)被采集到数仓的ODS层中,进入DIM层的时候将2024-01-12日修改过的老状态的数据(也就是过期数据)结束时间设置为前一天(标志该条数据生命周期结束),并放入前一天的分区中,而新增的数据和没有修改(没有修改过,那么这条数据的状态目前也是最新数据)过的数据放入到9999-12-31分区中,表示这张表最新状态的数据。

在这里插入图片描述

实现

1. 初始化拉链表

  第一次向拉链表中导入数据的时候直接将ODS层中所有的数据overwrite到9999-12-31分区中就可以了,因为那天的数据就是最新的数据。

insert overwrite table dim_user_zip partition(dt="9999-12-31")
--insert overwrite local directory "ods_user2"
select
data.id,
data.login_name,
data.nick_name,
data.name,
data.phone_num,
data.email,
data.user_level,
data.birthday,
data.gender,
data.create_time,
data.operate_time,
date_format(nvl(data.operate_time,data.create_time),"yyyy-MM-dd") start_time,
"9999-12-31" end_time
from ods_user_info_inc
where dt="2024-01-11" and type="bootstrap-insert"

这步完成后就初始化完成了拉链表,也就对应上图中左上角那个 “该表9999-12-31分区原来的数据” 表中的数据。

2. 后续拉链表数据的更新

方式1:
新增数据和原来分区的数据进行 full join 然后判断选择要哪条数据,然后overwrite到表中就行了

with new as (select *,"2024-01-12" start_date,"9999-12-31" end_datefrom ods_user_info_incwhere dt = "2024-01-12"
), old as (select *from dim_user_zip_incwhere dt = "9999-12-31"
), full_user as (select old表的所有字段,new表的所有字段from old full join newon old.id=new.id
)
-- 将数据更新到dim层的拉链表中,这里采用动态分区,按最后一列选插入到哪个分区
insert overwrite table dim_user_zip_inc partition(dt)
select if(new_id is not null, new_id, old_id),......--取完表中的字段后,要多加一个字段,用来动态分区到哪个分区中,最新的数据要放入9999-12-31分区if(new_id is not null, new_end_date, old_end_date)
from full_user
-- 这是筛选出新增的数据和没有修改过的数据
where new_id is not null or (new_id is null and old_id is not null)
union all
select选出老数据的字段,注意最后一个失效时间要改成前一天cast (date_sub("2024-01-12", 1) as string),-- 最后还是要多加一个字段,用来动态分区到哪个分区中,过期的数据要放入前一天分区cast (date_sub("2024-01-12", 1) as string)
from full_user
-- 这是筛选出修改过的老数据
where new_id is not null and old_id is not null;========================================================================with new as (select id,login_name,nick_name,name,phone_num,email,user_level,birthday,gender,create_time,operate_time,start_date,end_datefrom (selectdata.id,data.login_name,data.nick_name,data.name,data.phone_num,data.email,data.user_level,data.birthday,data.gender,data.create_time,data.operate_time,"2024-01-12" start_date,"9999-12-31" end_date,row_number() over (partition by data.id order by ts desc) rnfrom ods_user_info_incwhere dt = "2024-01-12") t1where rn = 1), old as(select id,login_name,nick_name,name,phone_num,email,user_level,birthday,gender,create_time,operate_time,start_date,end_datefrom dim_user_zipwhere dt="9999-12-31"
), full_user as(selectold.id old_id,old.login_name old_login_name,old.nick_name old_nick_name,old.name old_name,old.phone_num old_phone_num,old.email old_email,old.user_level old_user_level,old.birthday old_birthday,old.gender old_gender,old.create_time old_create_time ,old.operate_time old_operate_time ,old.start_date old_start_date,old.end_date old_end_date,new.id new_id,new.login_name new_login_name,new.nick_name new_nick_name,new.name new_name,new.phone_num new_phone_num,new.email new_email,new.user_level new_user_level,new.birthday new_birthday,new.gender new_gender,new.create_time new_create_time ,new.operate_time new_operate_time ,new.start_date new_start_date,new.end_date new_end_datefrom old full join new on old.id=new.id
)
insert overwrite table dim_user_zip partition(dt)
selectif(new_id is not null,new_id,old_id),if(new_id is not null,new_login_name,old_login_name),if(new_id is not null,new_nick_name,old_nick_name),if(new_id is not null,new_name,old_name),if(new_id is not null,new_phone_num,old_phone_num),if(new_id is not null,new_email,old_email),if(new_id is not null,new_user_level,old_user_level),if(new_id is not null,new_birthday,old_birthday),if(new_id is not null,new_gender,old_gender),if(new_id is not null,new_create_time,old_create_time),if(new_id is not null,new_operate_time,old_operate_time),if(new_id is not null,new_start_date,old_start_date),if(new_id is not null,new_end_date,old_end_date),if(new_id is not null,new_end_date,old_end_date)
from full_user where new_id is not null or (new_id is null and old_id is not null)
union all
selectold_id,old_login_name,old_nick_name,old_name,old_phone_num,old_email,old_user_level,old_birthday,old_gender,old_create_time ,old_operate_time ,old_start_date,cast(date_sub("2024-01-12",1) as string),cast(date_sub("2024-01-12",1) as string)
from full_user where new_id is not null and old_id is not null;

方式二:
将旧数据和新数据都查出来然后union all到一起,然后根据用户id和start_time倒叙排序,编号为1的就是最新的数据,放到最新的分区,否则就是过期数据放到前一天的分区

with new as(-- 取出当天修改的最后一条结果select *,'2024-01-12' start_time,"9999-12-31" end_timefrom (select*,row_number() over (partition by user_id order by ts desc) rnfrom ods_user_info_incwhere dt = '2024-01-12') t1where rn = 1
), old as(select*from dim_user_zip_incwhere dt = "9999-12-31"
), full_user as(select * from newunion allselect * from old
), ordered as(select *,row_number() over (partition by user_id order by start_time desc) rnfrom full_user
)
insert overwrite table dim_user_zip_inc partition(dt)
select *,if(rn=1,"9999-12-31",cast(date_sub("2024-01-12",1) as string)),if(rn=1,"9999-12-31",cast(date_sub("2024-01-12",1) as string))
from ordered==============================================================================with new as(-- 取出当天修改的最后一条结果select id,login_name,nick_name,name,phone_num,email,user_level,birthday,gender,create_time,operate_time,start_time,end_timefrom(selectdata.id,data.login_name,data.nick_name,data.name,data.phone_num,data.email,data.user_level,data.birthday,data.gender,data.create_time,data.operate_time,"2024-01-12" start_time,"9999-12-31" end_time,row_number() over (partition by data.id order by ts desc) rnfrom ods_user_info_incwhere dt = '2024-01-12') t1 where rn=1
), old as(select id,login_name,nick_name,name,phone_num,email,user_level,birthday,gender,create_time,operate_time,start_date,end_datefrom dim_user_zipwhere dt = "9999-12-31"
), full_user as(select * from newunion allselect * from old
), ordered as(select id,login_name,nick_name,name,phone_num,email,user_level,birthday,gender,create_time,operate_time,start_time,end_time,row_number() over (partition by id order by start_time desc) rnfrom full_user
)
insert overwrite table dim_user_zip partition(dt)
--insert overwrite local directory "dim_user_zip2"
select id,login_name,nick_name,name,phone_num,email,user_level,birthday,gender,create_time,operate_time,start_time,if(rn=1,end_time,date_sub('2024-01-12',1)),if(rn=1,'9999-12-31',cast(date_sub('2024-01-12',1) as string))
from ordered

这样就完成了拉链表的制作,包括拉链表的初始化和后续拉链表数据的更新,以后只需要改里面的时间就可以了。

总结

拉链表第一次导入数据就都是最新状态的数据,然后新采集到的数据和最新状态的数据join后将最新状态的数据写入最新的分区,过期数据写入前一天的分区,注意日期不要交叉。

踩坑:脚本中日期引用不要使用双引号,使用单引号就行了,也就是sql中变量字符等用单引号,双引号写入脚本中,最后再套一个双引号有问题。

在这里插入图片描述

FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.MoveTask. Exception when loading 2 in table dim_user_zip with loadPath=hdfs://hadoop101:8020/warehouse/gmall/dim/dim_user_zip/.hive-staging_hive_2024-02-16_14-55-57_153_8417650511018457362-1/-ext-10000
彩蛋 - 想清空表的数据:转成内部表,清空数据后,再转成外部表,将分区目录删掉,然后再次跑脚本,其他表都没问题就拉链表新算出过期分区的数据拉不进去,这是啥原因?有高人指点一下吗?

我目前的解决方案是:删除了表然后重新建下就好了。

我查的原因是文件有特殊字符(这个不太可能,同样的数据重建表就能,应该不是数据问题),修复元数据也没用,分区字段有问题(这个也没问题,我检查了),重建元数据库(这个不靠谱,没试)搞了好久,没找到根本原因,放弃了,有大哥知道的话,麻烦指点一下🎈

这篇关于Hive拉链表设计、实现、总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/715608

相关文章

Flutter实现文字镂空效果的详细步骤

《Flutter实现文字镂空效果的详细步骤》:本文主要介绍如何使用Flutter实现文字镂空效果,包括创建基础应用结构、实现自定义绘制器、构建UI界面以及实现颜色选择按钮等步骤,并详细解析了混合模... 目录引言实现原理开始实现步骤1:创建基础应用结构步骤2:创建主屏幕步骤3:实现自定义绘制器步骤4:构建U

SpringBoot中四种AOP实战应用场景及代码实现

《SpringBoot中四种AOP实战应用场景及代码实现》面向切面编程(AOP)是Spring框架的核心功能之一,它通过预编译和运行期动态代理实现程序功能的统一维护,在SpringBoot应用中,AO... 目录引言场景一:日志记录与性能监控业务需求实现方案使用示例扩展:MDC实现请求跟踪场景二:权限控制与

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义