TRANSFORM
描述
TRANSFORM
子句用于指定 Hive 风格的转换查询规范,通过运行用户指定的命令或脚本来转换输入。
Spark 的脚本转换支持两种模式
- Hive 支持禁用:Spark 脚本转换可以在
spark.sql.catalogImplementation=in-memory
的情况下运行,或者在没有SparkSession.builder.enableHiveSupport()
的情况下运行。在这种情况下,Spark 目前只使用带有ROW FORMAT DELIMITED
的脚本转换,并将传递给脚本的所有值视为字符串。 - Hive 支持启用:当 Spark 在
spark.sql.catalogImplementation=hive
的情况下运行,或者 Spark SQL 通过SparkSession.builder.enableHiveSupport()
启动时,Spark 可以同时使用 Hive SerDe 和ROW FORMAT DELIMITED
进行脚本转换。
语法
SELECT TRANSFORM ( expression [ , ... ] )
[ ROW FORMAT row_format ]
[ RECORDWRITER record_writer_class ]
USING command_or_script [ AS ( [ col_name [ col_type ] ] [ , ... ] ) ]
[ ROW FORMAT row_format ]
[ RECORDREADER record_reader_class ]
参数
-
表达式
指定一个或多个值、操作符和 SQL 函数的组合,其结果是一个值。
-
行格式
指定输入和输出的行格式。有关更多语法细节,请参阅 HIVE FORMAT。
-
RECORDWRITER
指定自定义 RecordWriter 的完全限定类名。默认值为
org.apache.hadoop.hive.ql.exec.TextRecordWriter
。 -
RECORDREADER
指定自定义 RecordReader 的完全限定类名。默认值为
org.apache.hadoop.hive.ql.exec.TextRecordReader
。 -
命令或脚本
指定用于处理数据的命令或脚本路径。
ROW FORMAT DELIMITED 行为
当 Spark 使用 ROW FORMAT DELIMITED
格式时
- Spark 使用字符
\u0001
作为默认字段分隔符,该分隔符可以通过FIELDS TERMINATED BY
进行覆盖。 - Spark 使用字符
\n
作为默认行分隔符,该分隔符可以通过LINES TERMINATED BY
进行覆盖。 - Spark 使用字符串
\N
作为默认的NULL
值,以区分NULL
值和字面字符串NULL
。该分隔符可以通过NULL DEFINED AS
进行覆盖。 - Spark 在将所有列传递给用户脚本之前,会将其转换为
STRING
类型并用制表符组合。对于ARRAY
/MAP
/STRUCT
等复杂类型,Spark 使用to_json
将其转换为输入JSON
字符串,并使用from_json
将结果输出JSON
字符串转换回ARRAY
/MAP
/STRUCT
数据。 COLLECTION ITEMS TERMINATED BY
和MAP KEYS TERMINATED BY
是用于拆分ARRAY
/MAP
/STRUCT
等复杂数据的分隔符,Spark 使用to_json
和from_json
以JSON
格式处理复杂数据类型。因此,在默认行格式下,COLLECTION ITEMS TERMINATED BY
和MAP KEYS TERMINATED BY
不会起作用。- 用户脚本的标准输出被视为制表符分隔的
STRING
列。任何仅包含字符串\N
的单元格都将被重新解释为字面NULL
值,然后生成的STRING
列将被转换为col_type
中指定的数据类型。 - 如果实际输出列的数量少于指定输出列的数量,则额外的输出列将用
NULL
填充。例如output tabs: 1, 2 output columns: A: INT, B INT, C: INT result: +---+---+------+ | a| b| c| +---+---+------+ | 1| 2| NULL| +---+---+------+
- 如果实际输出列的数量多于指定输出列的数量,则输出列只选择对应的列,其余部分将被丢弃。例如,如果输出有三个制表符,但只有两个输出列
output tabs: 1, 2, 3 output columns: A: INT, B INT result: +---+---+ | a| b| +---+---+ | 1| 2| +---+---+
- 如果在
USING my_script
之后没有AS
子句,则输出模式为key: STRING, value: STRING
。key
列包含第一个制表符之前的所有字符,value
列包含第一个制表符之后的所有剩余字符。如果没有制表符,Spark 将返回NULL
值。例如output tabs: 1, 2, 3 output columns: result: +-----+-------+ | key| value| +-----+-------+ | 1| 2| +-----+-------+ output tabs: 1, 2 output columns: result: +-----+-------+ | key| value| +-----+-------+ | 1| NULL| +-----+-------+
Hive SerDe 行为
当启用 Hive 支持并使用 Hive SerDe 模式时
- Spark 默认使用 Hive SerDe
org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
,因此在将列传递给用户脚本之前,它们会被转换为STRING
并用制表符组合。 - 所有字面
NULL
值都被转换为字符串\N
,以便区分字面NULL
值和字面字符串NULL
。 - 用户脚本的标准输出被视为制表符分隔的
STRING
列,任何仅包含字符串\N
的单元格都将被重新解释为NULL
值,然后生成的STRING
列将被转换为col_type
中指定的数据类型。 - 如果实际输出列的数量少于指定输出列的数量,则额外的输出列将用
NULL
填充。 - 如果实际输出列的数量多于指定输出列的数量,则输出列只选择对应的列,其余部分将被丢弃。
- 如果在
USING my_script
之后没有AS
子句,则输出模式为key: STRING, value: STRING
。key
列包含第一个制表符之前的所有字符,value
列包含第一个制表符之后的所有剩余字符。如果没有制表符,Spark 将返回NULL
值。 - 这些默认值可以通过
ROW FORMAT SERDE
或ROW FORMAT DELIMITED
覆盖。
示例
CREATE TABLE person (zip_code INT, name STRING, age INT);
INSERT INTO person VALUES
(94588, 'Zen Hui', 50),
(94588, 'Dan Li', 18),
(94588, 'Anil K', 27),
(94588, 'John V', NULL),
(94511, 'David K', 42),
(94511, 'Aryan B.', 18),
(94511, 'Lalit B.', NULL);
-- With specified output without data type
SELECT TRANSFORM(zip_code, name, age)
USING 'cat' AS (a, b, c)
FROM person
WHERE zip_code > 94511;
+-------+---------+-----+
| a | b| c|
+-------+---------+-----+
| 94588| Anil K| 27|
| 94588| John V| NULL|
| 94588| Zen Hui| 50|
| 94588| Dan Li| 18|
+-------+---------+-----+
-- With specified output with data type
SELECT TRANSFORM(zip_code, name, age)
USING 'cat' AS (a STRING, b STRING, c STRING)
FROM person
WHERE zip_code > 94511;
+-------+---------+-----+
| a | b| c|
+-------+---------+-----+
| 94588| Anil K| 27|
| 94588| John V| NULL|
| 94588| Zen Hui| 50|
| 94588| Dan Li| 18|
+-------+---------+-----+
-- Using ROW FORMAT DELIMITED
SELECT TRANSFORM(name, age)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
NULL DEFINED AS 'NULL'
USING 'cat' AS (name_age string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '@'
LINES TERMINATED BY '\n'
NULL DEFINED AS 'NULL'
FROM person;
+---------------+
| name_age|
+---------------+
| Anil K,27|
| John V,null|
| ryan B.,18|
| David K,42|
| Zen Hui,50|
| Dan Li,18|
| Lalit B.,null|
+---------------+
-- Using Hive Serde
SELECT TRANSFORM(zip_code, name, age)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
WITH SERDEPROPERTIES (
'field.delim' = '\t'
)
USING 'cat' AS (a STRING, b STRING, c STRING)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
WITH SERDEPROPERTIES (
'field.delim' = '\t'
)
FROM person
WHERE zip_code > 94511;
+-------+---------+-----+
| a | b| c|
+-------+---------+-----+
| 94588| Anil K| 27|
| 94588| John V| NULL|
| 94588| Zen Hui| 50|
| 94588| Dan Li| 18|
+-------+---------+-----+
-- Schema-less mode
SELECT TRANSFORM(zip_code, name, age)
USING 'cat'
FROM person
WHERE zip_code > 94500;
+-------+----------------+
| key| value|
+-------+----------------+
| 94588| Anil K 27|
| 94588| John V \N|
| 94511| Aryan B. 18|
| 94511| David K 42|
| 94588| Zen Hui 50|
| 94588| Dan Li 18|
| 94511| Lalit B. \N|
+-------+----------------+