“transform”如何影响存储到磁盘和从磁盘读取的值?
我正在尝试理解在Hive中如何处理空值,特别是在Python转换中检查空值的情况。
我一直在阅读关于转换的这个维基页面:
https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Transform
我特别关注第二段内容:
默认情况下,列会被转换为字符串,并用制表符分隔,然后传递给用户脚本;同样,所有的空值会被转换为字面字符串 \N,以便区分空值和空字符串。用户脚本的标准输出会被视为用制表符分隔的字符串列,任何仅包含 \N 的单元格会被重新解释为空值,然后结果字符串列会按照表声明中指定的数据类型进行转换。用户脚本可以将调试信息输出到标准错误,这些信息会在Hadoop的任务详情页面上显示。这些默认设置可以通过 ROW FORMAT .... 进行覆盖。
但我有点困惑,当它说“所有空值”时,它是如何判断一个值是否为空的?它是否足够聪明,能够考虑到“serialization.null.format”这个表属性?
如果我理解文档没错,我认为默认行为大概是这样的:
要存储到磁盘的值 => 实际存储到磁盘的值 => 通过 TRANSFORM 发送到 Python 的值
注意 \N
不是转义序列,而是字面上的两个字符:反斜杠加大写字母N。
- (空字符串) => (空字符串) => "
\N
" - (空值) => (空字符串) => "
\N
" - 'NULL' => 'NULL' => 'NULL'
- "
\N
" => "\N
" => "\N
"
如果对这个理解有任何纠正,那就太好了。:)
不过,我并不是在处理默认情况。我正在一个系统上工作,所有表都设置了这个表属性:
TBLPROPERTIES ('serialization.null.format'='NULL')
从文档中我并不清楚这将如何影响这些值的存储方式,以及它们在 TRANSFORM 过程中如何被转换并发送到 Python:
要存储到磁盘的值 => 实际存储到磁盘的值 => 通过 TRANSFORM 发送到 Python 的值
再次强调,\N
不是转义序列,而是字面上的两个字符:反斜杠加大写字母N。
- (空字符串) => (空字符串) => ???
- (空值) => 'NULL' => ???
- 'NULL' => 'NULL' => ???
- "\N" => "\N" => "\N"
所以我主要的问题是,有人能提供一些关于第二种情况的见解或澄清吗?我需要知道这些值在Python中会是什么样子,以便我可以正确检查这个值是否为空。
1 个回答
根据文档的内容,我了解到Hive的Transform功能根本不使用“serialization.null.format”这个表属性。它有自己处理空值的方法:
value in table | given to python script
---------------------------------------
empty string | empty string
null value | \N
NULL | NULL
\N | \N
所以你需要处理那些可能包含\N
作为值的列,就像你需要处理任何可能包含制表符的列一样。在相关页面上有明确的警告:
警告:在转换之前,你有责任处理任何字符串列。如果你的字符串列包含制表符,身份转换器不会给你返回你最开始的数据!为了解决这个问题,可以使用REGEXP_REPLACE,把制表符替换成其他字符,然后再传入TRANSFORM()调用中。
对于所有非字符串列来说,一切都很好:它们不能包含制表符或\N
,Python脚本只需要把\N
当作空值来处理。
如果你有字符串列,并且你确定它们既不包含制表符也不包含\N
,那也没问题,这种情况和前面一样,空字符串就是...空字符串!
如果一个字符串列可能包含制表符,但有另一个字符不能出现(比如|
),你可以在MAP子句中通过regexp_replace
把制表符替换成|
,那么Python脚本就需要知道这个列中的|
代表的是制表符,然后在REDUCE子句中使用反向的regexp_replace
。
如果一个字符串列可以包含\N
,但有另一个字符串不能出现,那么可以应用前面的规则。
在更一般的情况下,我认为唯一可靠的解决方案是把空值转换为\N
,并把所有非空值编码为base64(\N
希望不是有效的base64值...),可以使用类似if (A IS NULL, '\N', base64(binary(A)))
的方式来实现,解码时使用if (A = '\N', NULL, cast(unbase64(A) as STRING))
。