使用hive时,我们偶尔会遇到这样的问题,当你将结果输出到屏幕时,查出的数据往往显示为null,但是当你将结果输出到文本时,却显示为空(即未填充),这是为什么呢?
在hive中有一种假NULL,它看起来和NULL一样,但是实际却不是NULL。
比如下面这样:
[hdfs@dsdc04 ~]$ cat /data6/chenye/baidu_djzs_sessionid/test
\N
NULL
Kris
null
本地的test文件内有五行数据,分别为\N、NULL、 、Kris、null
我们建表将它传到hdfs上,之后再在hive中进行查询,结果如下:
hive> select * from basedatadb.testnull;
OK
NULL
NULL
Kris
null
Time taken: 0.748 seconds, Fetched: 5 row(s)
看清楚了没?发生了什么?在hive中查询时,也显示为五行数据,但是它们分别为NULL、NULL、 、Kris、null。为什么原始数据中的\N在hive中变成了NULL?
再看看,我们本次将结果输出到文本时的情况:
hive> insert overwrite local directory '/data/testnull'
> row format delimited fields terminated by ','
> select *
> from basedatadb.testnull;
Query ID = hdfs_20151020105050_650b3be9-e1af-4236-af4d-71283fb2642b
Total jobs = 1
Launching Job 1 out of 1
Status: Running (Executing on YARN cluster with App id application_1441787030155_0736)
--------------------------------------------------------------------------------
VERTICES STATUS TOTAL COMPLETED RUNNING PENDING FAILED KILLED
--------------------------------------------------------------------------------
Map 1 .......... SUCCEEDED 1 1 0 0 0 0
--------------------------------------------------------------------------------
VERTICES: 01/01 [==========================>>] 100% ELAPSED TIME: 6.39 s
--------------------------------------------------------------------------------
Copying data to local directory /data/testnull
Copying data to local directory /data/testnull
OK
Time taken: 10.633 seconds
------------
打开这个结果显示:
[hdfs@dsdc04 ~]$ cat /data/testnull/*
\N
NULL
Kris
null
也显示为五行,分别为\N、NULL、 、Kris、null。
这种假NULL产生的原因是因为在hive中,空值NULL在底层默认是用'\N'来存储的。
这样的设计存在一个问题是如果实际想存储'\N',那么实际查询出来的也是NULL而不是'\N' 。
Hive给出一种并非完美的解决方法就是可以自定义底层用什么字符来表示NULL。
例如我想用字符'Kris'来表示NULL:
hive> alter table basedatadb.testnull set SERDEPROPERTIES('serialization.null.format' = 'Kris');
OK
Time taken: 0.303 seconds
hive> select * from basedatadb.testnull;
OK
\N
NULL
NULL
null
Time taken: 0.131 seconds, Fetched: 5 row(s)
hive>
看到没有,我们将本张表的空值标识设定为“Kris”后,原本的\N正常显示为\N,原本的Kris显示为NULL。
今后插入到这张表中的'Kris'查询出来就变成了NULL而不是'Kris' 。因为我这个版本的hive还不支持插入操作,所以此处不实验。
然后我又很好奇我们把结果输出到文本会是什么情况,于是我又开启了相关实验,发现:
hive> insert overwrite local directory '/data/testnull2'
> row format delimited fields terminated by ','
> select *
> from basedatadb.testnull;
Query ID = hdfs_20151020113333_9bcd875a-64d7-4494-98d5-aca2af76be3a
Total jobs = 1
Launching Job 1 out of 1
Status: Running (Executing on YARN cluster with App id application_1441787030155_0739)
--------------------------------------------------------------------------------
VERTICES STATUS TOTAL COMPLETED RUNNING PENDING FAILED KILLED
--------------------------------------------------------------------------------
Map 1 .......... SUCCEEDED 1 1 0 0 0 0
--------------------------------------------------------------------------------
VERTICES: 01/01 [==========================>>] 100% ELAPSED TIME: 6.15 s
--------------------------------------------------------------------------------
Copying data to local directory /data/testnull2
Copying data to local directory /data/testnull2
OK
Time taken: 10.921 seconds
hive> Shutting down tez session.
[hdfs@dsdc04 ~]$ cat ^C
[hdfs@dsdc04 ~]$ cat /data/testnull2/*
\N
NULL
\N
null
当我把表的SET SERDEPROPERTIES属性更改为'Kris'后,当我将查询结果输出到文本时,Kris也显示为\N。
结论:
其实上面说的这个假NULL出现的原因就是在默认情况下,即用'\N'表示NULL。可以用SET SERDEPROPERTIES语句修改表的属性,可将其他字段设置为默认的空值的表示。但是值得注意的是:底层存储的文件并没有被修改,经过hive查询和计算的文件却修改了。而'\N'显示为NULL在Hive中又是一个特例,于是就出现了这个假NULL,在开发及分析过程中一定要注意!
以上资源来源于:http://blog.sina.com.cn/s/blog_6ff05a2c010131sh.html