Hive中有種假NULL,它看起來和NULL一摸一樣,但是實際卻不是NULL。
例如如下這個查詢:
hive> desc ljn004;
OK
a string
Time taken: 0.237 seconds
hive> select a from ljn004;
OK
NULL
Time taken: 46.232 seconds
看上去好像ljn004的a字段保存了一個 NULL,
但是換一個查詢會發現它和NULL並不一樣:
hive> select a from ljn004 where a is null;
OK
Time taken: 62.56 seconds
來看一下實際存儲的是什么:
hive> select * from ljn004;
OK
\N
Time taken: 1.232 seconds
hive> select a from ljn004 where a = '\\N';
OK
NULL
Time taken: 72.933 seconds
ljn004的a字段實際存儲的是一個'\N',a = '\\N'是因為Hive中'\'是轉義字符,需要對'\'進行一次轉義,所以變成'\\N'。
這種假NULL產生的原因實際上源於對表的錯誤操作。在Hive中,空值NULL在底層默認是用'\N'來存儲的,看一個例子:
hive> create table ljn005 (col1 string);
OK
Time taken: 1.258 seconds
1 Rows loaded to ljn005
OK
Time taken: 63.727 seconds
hive> insert overwrite table ljn005 select NULL from dual;
然后看一下底層的數據存儲:
$ hadoop fs -cat /group/hive/ljn005/attempt_201205041518_256192_m_000000_0
\N
可以看到底層數據將NULL存儲成了'\N' 。
這樣的設計存在一個問題是如果實際想存儲'\N',那么實際查詢出來的也是NULL而不是'\N' 。
Hive給出一種並非完美的解決方法就是可以自定義底層用什么字符來表示NULL。
例如我想用字符'a'來表示NULL:
hive> alter table ljn005 SET SERDEPROPERTIES('serialization.null.format' = 'a');
OK
Time taken: 0.175 seconds
hive> insert overwrite table ljn005 select NULL from dual;
1 Rows loaded to ljn005
OK
Time taken: 62.66 seconds
再看一下底層的存儲:
$ hadoop fs -cat /group/hive/ljn005/attempt_201205041518_256764_m_000000_0
a
這時候底層的存儲就變成了'a' ,今后插入到這張表中的'a'查詢出來就變成了NULL而不是'a' 。
其實上面說的這個假NULL出現的原因就是在默認情況下(即用'\N'表示NULL),插入了NULL值,然后又用SET SERDEPROPERTIES語句修改了存儲NULL的字符串。這時候表的屬性修改了,但是底層存儲的文件並沒有修改。而'\N'顯示為NULL在Hive中又是一個特例,於是就出現了這個假NULL,在開發過程中一定要注意!