“transform”如何影响存储到磁盘和从磁盘读取的值?

1 投票
1 回答
515 浏览
提问于 2025-04-18 10:34

我正在尝试理解在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 个回答

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))

撰写回答