hive中NULL值问题
问题描述
源端数据oracle数据库,通过cdm迁移工具将数据迁移到目标端hive。在oracle中的NULL值迁移到hive中后有的字段表现为NULL,有的字段表现为空串“”(即两个引号中间为空)。观察发现字符型的数据字段为空串,非字符型的字段为NULL。
整个链路涉及到了oracle、cdm、hive,分析问题的原因就从这三个产品着手。首先我们知道oracle中没有空串,当插入空串时写入的是NULL。很明显hive与oracle不同,hive中可以写入空串也可以写入NULL,空串和NULL在hive中是两个不同的概念。
你可能不了解cdm,它是华为云的云数据迁移(Cloud Data Migration,简称CDM)工具。
hive中什么数据会为NULL
hive中表的存储格式为file类型(如textfile、rcfile),它们在HDFS中会把NULL值存储为‘\N’
CREATE TABLE `test_rcfile`( `id` int, `name` string) STORED AS rcfile; insert into test_rcfile select 1,'abc' union all select '','abc' union all select 3,'' union all select '\\N','\\N' union all select '\N','\N' union all select 'NULL','NULL' union all select 7,NULL; |
select * from test_rcfile; |
select * from test_rcfile where id is NULL; |
select * from test_rcfile where name is NULL; |
看一下在HDFS中存储的数据(这是按列存储的) |
解释一下上面执行结果:
在insert操作的时候hive会对字段类型进行模式匹配,不符合类型的数据会插入NULL值。对于int类型的id字段,当插入的是字符串或者空串的时候它都不符合int类型,所以插入的就是NULL,在HDFS中存储的就是‘\N’。
当插入的是字符类型的字段时,空串被当做一个字符对待,所以hive中完全可以存储空串,空串并不会作为NULL值处理,空串在HDFS中就是存储的空串“”。
CREATE TABLE `test_textfile`( `id` int, `name` string) STORED AS textfile; insert into test_textfile select 1,'abc' union all select '','abc' union all select 3,'' union all select '\\N','\\N' union all select '\N','\N' union all select 'NULL','NULL' union all select 7,NULL; |
select * from test_rcfile; select * from test_rcfile where id is NULL; select * from test_rcfile where name is NULL 查询结果和rcfile一样。 |
看一下HDFS中存储的数据 |
如果是load导入数据会怎样,先看一下文本数据 将数据上传HDFS:hdfs dfs -put test /tmp/.testHDFS/t 在hive中执行load导入数据到表:load data inpath '/tmp/.testHDFS/t' overwrite into table test_rcfile; 看下HDFS中存的数据 查询数据:select * from test_textfile where id is null; |
解释一下为什么存储和查询不一样:
load导入数据是不会检查数据的类型是否匹配的,它只是简单的转移数据操作,所以速度很快。它会在select的时候检查数据类型模式。所以虽然HDFS中id字段存储的是“xyz”,查询出来却是NULL,而且where条件可以筛选出来。
而对于字符型的数据类型,它可以涵盖其他类型的数据,就像是隐式转换这种概念一样,其他类型可以转换成字符型的。
CREATE TABLE `test_orc`( `id` int, `name` string) STORED AS orc; insert into test_orc select 1,'abc' union all select '','abc' union all select 3,'' union all select '\\N','\\N' union all select '\N','\N' union all select 'NULL','NULL' union all select 7,NULL; |
select * from test_orc; |
select * from test_orc where id is null; |
select * from test_orc where name is null; |
orc格式和file类型对NULL是不同的。file类型会把NULL在HDFS中写成“\N”,orc格式并非如此,orc格式中“\N”就是普通的字符串。
总结一下:
1、file格式中NULL在HDFS中以“\N”形式存储。orc格式中并不是这样。
2、load方式导入数据时不会检查数据类型,数据以原有形式存储。它会在查询的时候检查字段类型是否匹配,不匹配的为NULL。
3、insert方式插入数据时就会检查数据字段类型是否匹配,会将不匹配的数据以NULL值存储。
如何让hive中的空串变成NULL
ALTER TABLE test_rcfile SET SERDEPROPERTIES('serialization.null.format' = ''); |
select * from test_rcfile; |
select * from test_rcfile where name is null; |
select * from test_rcfile where name=''; |
修改表的配置,使得原来NULL以“\N”存储变成了以空串存储。此时在HDFS中存储的“\N”会被以字符串的形式打印,空串也会被解析成NULL。如果新insert的数据为空串或者NULL,在HDFS中将以空(字段分隔符中间什么也没有)存储。
textfile格式同rcfile情况一样。orc格式却不同,即使修改'serialization.null.format' = ''也没有变化,它并不是以空串存储也不是以“\N”存储。
需要注意的是这个参数只能在表级修改,在database级、hive产品级别并没有相似的参数。所以如果通过修改参数达到所有空串解析为NULL的效果,就得对需要的所有表进行修改。
总结一下:
1、通过修改参数我们把file格式的空串解析成NULL,orc格式不会改变。
2、修改参数后用空串作为查询条件查询结果为空。因为空串被解析成NULL了。
3、这个参数只能表级修改。
通过修改hive端的参数解决空串问题并不是那么理想:它要单张表修改参数,维护成本高;只能改变file格式的问题,orc格式问题仍旧存在。
那可不可以在cdm写入hive端的时候进行操作呢。
cdm可以插入NULL值吗
在sqoop中可以采用两个参数设置:
--null-string '\\N' (把所有string类型的空值转成hive的null值'\N')
--null-non-string '\\N' (把非string类型的空值转换成hive的null值'\N')
通过上面这两个参数会把字符类型和非字符类型的空串转换成‘\N’写入到hive表中,然后所查询的数据就没有空串了,源端是NULL值的在hive的数据也是NULL。
sqoop可以通过参数设置,那么cdm可以吗?非常遗憾cdm并没有此类参数,虽然cdm底层是sqoop。并不是cdm做不到,而是因为cdm出于产品安全的考虑,它为了避免参数注入问题就不提供参数设置功能。
cdm针对hive不同格式会有怎样的迁移过程呢。测试情况如下。
源端数据库为oracle数据库。源端数据为: |
目标端表分别为上面用到过的test_textfile、test_rcfile、test_orc; 分别创建相应的cdm迁移作业。 |
select * from test_textfile; 目标端少了2条数据:存在NULL值的数据没有迁移。 cdm日志中记录:java.lang.NullPointerException: NULL |
select * from test_rcfile; |
select * from test_orc; 结果同查询test_rcfile相似,数据相同,只是顺序不同。 |
看上去rcfile和orc格式的表通过cdm迁移可以保持NULL不变空串。至于textfile格式中的空指针问题是cdm的bug,可以打补丁处理,不必过多关注textfile类型。
非分区表情况下rcfile和orc格式可以解决NULL值问题,那么hive分区表又有怎样的情况呢?我们测试下hive中分区表是否一样可行。
CREATE TABLE `test_textfile_par`(`id` int,`name` string) partitioned by (rfq char(8)) STORED AS textfile; CREATE TABLE `test_rcfile_par`(`id` int,`name` string) partitioned by (rfq char(8)) STORED AS rcfile CREATE TABLE `test_orc_par`(`id` int,`name` string) partitioned by (rfq char(8)) STORED AS orc; |
创建cdm作业 |
select * from test_textfile_par; |
select * from test_rcfile_par; 查询结果和textfile查询结果相同。 |
select * from test_orc_par; |
这种情况和直接用insert插入时的效果差不多。cdm在迁移到hive分区表时并没有解决NULL值问题。
解释一下:
cdm在处理hive的分区表和非分区表的时候是不同的。非分区表采用的是MapReduce中的map操作,分区表时采用的是map+reduce操作。
非分区表在map阶段可以直接调用HDFS的write接口将数据写入相应地址中。
hive分区表中分区会存储在相应文件目录中,hive会构建分区级别的目录。分区表需要根据分区字段的值合并(分区字段值相同的记录在物理上存放在相同的目录中),所以需要有reduce阶段。同时有map和reduce操作,中间数据就会有落地的操作,它为了防止出现空指针问题把NULL转成了空串处理。
问题解决了吗
总结一下:
问题涉及到的三个产品:oracle、cdm、hive。这个问题根本原因是因为oracle中无法存储空串,将空串视为NULL。而hive中可以存储空串,将空串视为一个字符。
oracle端我们无法更改,源端无法做处理。
目标端hive可以通过修改参数把NULL以空串存储,但这只适用于file格式,对orc格式不适用。另外修改参数是表级的,需要涉及的所有表进行修改,维护成本剧增。
cdm迁移过程中针对不同的hive格式和是否分区会有不同的迁移实现。目前非分区表的rcfile和orc格式可以成功写入NULL值。但是hive为分区表时写入的是空串。
解决方案
方案1:
所有表创建为rcfile格式的分区表,并设置NULL以空串形式存储。这样cdm迁移时会把NULL值全部转换为空串存储,而hive会把空串解析为NULL。
这种方案需要为每张表做参数修改。维护成本较高。
方案2:
在hive中字符类型的数据才会存在空串问题,非字符类型数据空串会被解析成NULL。可以在hiveSQL加工处理的时候进行数据清洗,将所有的空串转换为NULL。
这种方案会为每次加工时做处理,加入了一层数据清洗的工作。
建议:
这是oracle和hive的产品特性不同造成的问题,而且cdm在中间没有协调好。建议采用方案2。这可以看成一个脏数据问题,通过数据清洗就可以很好的解决问题。而方案1维护成本太高,非常容易出现开发不规范问题,即使出现问题也不易察觉。
hive中NULL值问题相关推荐
- mysql对null排序_mysql中null值的排序问题分析_MySQL
bitsCN.com mysql中null值的排序问题分析 如下表t_user: name age zhangsan 1 lisi NULL wangwu 2 执行一下sql: Sql代码 selec ...
- Sqoop导入hive中null是空字符串还是‘null‘的问题(关注)
Sqoop导入hive中null字符串处理为NULL 其中注意 这个是转为空字符串而不是'null'这种. 可以用ifnull转化的 这种就是'null'
- mysql中null值求和_sql求和涉及到null值
SQL ISNULL().NVL().IFNULL() 和 COALESCE() 函数 请看下面的 "Products" 表: P_Id ProductName UnitPrice ...
- hive 将null值替换为0_【Hive】数据倾斜
仰望星空,脚踏实地❤️ 唯有匠心,不负光阴❤️ 其实我是个暖男 你说你有点咳嗽 我立马帮你联系了花圈殡仪馆丧葬一条龙 怎么样,喜欢我这个事事有着落的男孩子吗? ??? 这次讨论的话题是 数据倾斜 先养 ...
- Hive中NULL的含义
在Hive中,NULL表示的是异常,与null不同,文件中的null在Hive中被认为字符串,如果对应的字段类型是字符类的,如string,则将其视为一个普通的字符串,而对于数据类的,则若数据本身是n ...
- Hive中NULL和''
前言 之前由于对Hive底层的对于NULL和''了解的不透彻,使用的时候出现了一些问题,今天闲来无事就整理一下 简单的测试案例 测试数据: 1,zs,23 2,NULL,24 3,, 4, ww,25 ...
- Oracle排序中NULL值处理的五种常用方法
1.缺省处理 Oracle在Order by 时缺省认为null是最大值,所以如果是ASC升序则排在最后,DESC降序则排在最前 2.使用nvl函数 nvl函数可以将输入参数为空时转换为一特定 ...
- MySQL从入门到精通50讲(十)-MySQL中null值如何处理
MySQL NULL 值处理 我们已经知道MySQL使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作. 为了 ...
- oracle排名怎么去除空值影响,Oracle排序中null值处理方法讲解
1.缺省处理 oracle在order by 时缺省认为null是最大值,所以如果是asc升序则排在最后,desc降序则排在最前 2.使用nvl函数 nvl函数可以将输入参数为空时转换为一特定值,如 ...
最新文章
- 网站SEO搜索排版布局优化
- Spring Ioc源码分析 之 Bean的加载(4):实例化Bean(createBeanInstance()方法)
- MySQL—常用指令总结
- 中文深度学习入门书:小白易入,课程、实战项目全有 | 五位导师联合出品
- Android 样式
- mschart mysql_在VB mschart里面可以一个MSCHART同时显示曲线和状图吗?
- php查询sql语句错误,Thinkphp3.2.3在SQL执行错误时查看SQL语句
- 涨价潮或延续至下半年 芯片国产化遇机遇
- python为什么被称为胶水语言_为什么只有python成了胶水语言?
- java题目汇总(一)
- 电商后台管理系统(一)
- 嵌入式 h.264中帧与场
- (1)前言-JsDroid引流脚本混合式开发技术系列教程By飞云脚本学院
- 51单片机DS18B20测温LCD12864显示
- 华三防火墙配置端口地址转换_H3C SecPath 防火墙设置之端口映射(命令)
- 按着Phoenix Framework文档练个手,感受一下Elixir语言。
- 崩坏星穹铁道PC、安卓、iOS三端配置要求
- 计算机科学计数法符号,科学计数法怎么表示
- Mozilla 开源稍后阅读应用 Pocket 代码
- SQL注入攻击的实现和防止