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 ]
參數
-
expression
指定一個或多個值、運算子與 SQL 函數的組合,會產生一個值。
-
row_format
指定輸入和輸出的列格式。請參閱 HIVE FORMAT 以取得更多語法詳細資料。
-
RECORDWRITER
指定自訂 RecordWriter 的完全限定類別名稱。預設值為
org.apache.hadoop.hive.ql.exec.TextRecordWriter
。 -
RECORDREADER
指定自訂 RecordReader 的完全限定類別名稱。預設值為
org.apache.hadoop.hive.ql.exec.TextRecordReader
。 -
command_or_script
指定一個命令或指令碼路徑來處理資料。
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
,並在提供給使用者指令碼之前,以 Tab 鍵合併欄位。對於複雜類型,例如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
在預設列格式中無法使用。- 使用者指令碼的標準輸出會被視為以 tab 分隔的
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| +---+---+------+
- 如果實際輸出的欄位數多於指定的輸出欄位數,輸出欄位只會選取對應的欄位,其餘部分會被捨棄。例如,如果輸出有三個 tab 而指定的輸出欄位只有兩個
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
欄位包含第一個 tab 之前的所有字元,而value
欄位包含第一個 tab 之後的所有字元。如果沒有 tab,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
並在傳送給使用者指令碼之前以 tab 結合。 - 所有文字
NULL
值都會轉換成字串\N
,以便區分文字NULL
值和文字字串NULL
。 - 使用者腳本的標準輸出視為以 tab 分隔的
STRING
欄,任何只包含字串\N
的儲存格都會重新詮釋為NULL
值,然後將產生的 STRING 欄轉換為col_type
中指定的資料類型。 - 如果實際輸出欄位數小於指定的輸出欄位數,會以
NULL
填入其他輸出欄位。 - 如果實際輸出欄位數大於指定的輸出欄位數,輸出欄位只會選取對應的欄位,其餘部分會捨棄。
- 如果在
USING my_script
之後沒有AS
子句,輸出結構會是key: STRING, value: STRING
。key
欄包含第一個 tab 之前的所有字元,而value
欄包含第一個 tab 之後的所有字元。如果沒有 tab,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|
+-------+---------------------+