このトピックでは、SELECT TRANSFORM
ステートメントを使用して、MaxCompute SQL が直接サポートしていない操作を実行する方法について説明します。 具体的には、SELECT TRANSFORM
を使用して指定した子プロセスを開始し、標準入力 (stdin) から必要な形式のデータを子プロセスに入力します。 次に、子プロセスの標準出力 (stdout) を解析して、最終出力を取得できます。
このプロセスでは、ユーザー定義関数 (UDF) をコンパイルする必要はありません。
SELECT TRANSFORM(arg1, arg2 ...)
(ROW FORMAT DELIMITED (FIELDS TERMINATED BY field_delimiter (ESCAPED BY character_escape)?)?
(LINES SEPARATED BY line_separator)?
(NULL DEFINED AS null_value)?)?
USING 'unix_command_line'
(RESOURCES 'res_name' (',' 'res_name')*)?
( AS col1, col2 ...)?
(ROW FORMAT DELIMITED (FIELDS TERMINATED BY field_delimiter (ESCAPED BY character_escape)?)?
(LINES SEPARATED BY line_separator)? (NULL DEFINED AS null_value)?)?
- キーワード
SELECT TRANSFORM
は、MAP
またはREDUCE
に置き換えても意味は変わりません。 ただし、構文を明確にするためSELECT TRANSFORM
の使用を推奨しています。 arg1,arg2...
は、TRANSFORM
句の引数を示します。TRANSFORM
句の引数の形式は、SELECT
句の項目の形式に似ています。 デフォルトの形式では、各引数の式の結果は暗黙的に文字列に変換された後、\t を使用して結合され、指定された子プロセスに入力されます。 デフォルトの形式は変更が可能です。 詳細については、次のセクションで説明するROW FORMAT
句をご参照ください。USING
:子プロセスを開始するコマンドを指定します。注- ほとんどの MaxCompute SQL ステートメントでは、
USING
句で指定することができるのはリソースのみです。 ただし、SELECT TRANSFORM
ステートメントでは、Hive 構文との互換性を確保するため、USING
句でコマンドを指定することができます。 USING
句の形式は、シェルスクリプトの構文に似ています。 ただし、子プロセスを開始するためのシェルスクリプトは実際には実行されず、子プロセスはコマンド入力に従って作成されます。 したがって、入力と出力のリダイレクト、パイプ、ループなどの多くのシェル機能は使用できません。 シェルスクリプトは、必要に応じて子プロセスのコマンドとして使用することができます。
- ほとんどの MaxCompute SQL ステートメントでは、
RESOURCES
:指定された子プロセスがアクセスできるリソースを指定します。 次の 2 つの方法のいずれかを使用して、リソースを指定します。RESOURCES
句を使用します。たとえば、using 'sh foo.sh bar.txt' Resources 'foo.sh','bar.txt'
を使用します。- SQL ステートメントの前に
set odps.sql.session.resources=foo.sh,bar.txt;
句を追加します。 この句が指定されるとグローバルに有効になります。すべての SELECT TRANSFORM ステートメントは、この句で指定されたリソースにアクセスできます。
ROW FORMAT
:入力または出力形式を指定します。構文には 2 つのROW FORMAT
句が含まれます。1 つは入力形式を指定し、もう 1 つは出力形式を指定します。 デフォルトでは、列の区切りには\t
が使用され、行の区切りには\n
が使用されます。Null
は\N
で表されます。注- field_delimiter、character_escape、line_separator で使用できるのは 1 文字のみです。 文字列を指定すると、文字列の最初の文字が他の文字よりも優先されます。
- Hive は各形式の構文を指定します。 MaxCompute では、inputRecordReader、outputRecordReader、Serde などの構文がサポートされています。
これらの形式を使用するには、SQL ステートメントの前に
set odps.sql.hive.compatible=true;
を追加して、Hive との互換性を有効にする必要があります。 Hive でサポートされている構文の詳細については、「Hive」をご参照ください。 - Hive でサポートされている inputRecordReader や outputRecordReader などの構文を指定すると、ステートメントの実行速度が低下する場合があります。
AS
:出力列を指定します。AS
句でデータ型を指定できます (例:as(col1:bigint, col2:boolean)
。as(col1, col2)
など、データ型を指定しない場合、デフォルトで文字列が返されます。- 出力は、子プロセスの標準出力を解析することにより取得します。 指定されたデータ型に STRING が含まれていない場合、システムは
CAST
関数を暗黙的に呼び出しますが、ランタイム例外が発生する場合があります。 - たとえば、
as(col1, col2:bigint)
のように、指定した列の一部のみにデータ型を指定することはできません。 AS
句をスキップすると、標準出力の最初の\t
の前のフィールドがキーになり、後続のすべてのパートが値になり、as(key, value)
と同等になります。
シェルスクリプトを呼び出す
SELECT TRANSFORM(script) USING 'sh' AS (data)
FROM (
SELECT 'for i in `seq 1 50`; do echo $i; done' AS script
) t
;
シェルコマンドは、TRANSFORM
句の入力として使用されます。
SELECT TRANSFORM は、単なる言語拡張機能ではありません。 AWK、Python、Perl、Shell などの単純な関数を使用すると、コマンドでスクリプトをコンパイルできるため、独立したスクリプトファイルをコンパイルしたり、リソースをアップロードしたりする必要がありません。
Python スクリプトを呼び出す
Python はコマンドでスクリプトをコンパイルできる単純な関数であるため、独立したスクリプトファイルをコンパイルしたり、リソースをアップロードしたりする必要はありません。 以下に、Python スクリプトを呼び出す方法の例を示します。
- Python スクリプトファイルをコンパイルします。 この例では、ファイル名は myplus.py です。
#!/usr/bin/env python import sys line = sys.stdin.readline() while line: token = line.split('\t') if (token[0] == '\\N') or (token[1] == '\\N'): print '\\N' else: print int(token[0]) + int(token[1]) line = sys.stdin.readline()
- Python スクリプトファイルをリソースとして MaxCompute に追加します。
add py ./myplus.py -f;
注 DataWorks コンソールを使用してリソースを追加することができます。 - SELECT TRANSFORM を使用して、リソースを呼び出します。
Create table testdata(c1 bigint,c2 bigint); - Creates a test table. insert into Table testdata values (1,4),(2,5),(3,6); - Inserts test data into the test table. - Execute the SELECT TRANSFORM statement: SELECT TRANSFORM (testdata.c1, testdata.c2) USING 'python myplus.py'resources 'myplus.py' AS (result bigint) FROM testdata; - Or: set odps.sql.session.resources=myplus.py; SELECT TRANSFORM (testdata.c1, testdata.c2) USING 'python myplus.py' AS (result bigint) FROM testdata;
+-----+
| cnt |
+-----+
| 5 |
| 7 |
| 9 |
+-----+
Python スクリプトでは、 MaxCompute をPython フレームワークで実行する必要がないため、フォーマット要件の対象ではありません。
TRANSFORM
句の入力として使用することができます。 たとえば、Python コマンドを実行してシェルスクリプトを呼び出すことができます。
SELECT TRANSFORM('for i in xrange(1, 50): print i;') USING 'python' AS (data);
Java スクリプトを呼び出す
Java スクリプトの呼び出しは、Python スクリプトの呼び出しに似ています。 この例では、Java スクリプトファイルをコンパイルし、.jar パッケージとしてエクスポートした後 add コマンドを実行して、.jar パッケージをリソースとして MaxCompute に追加する必要があります。 リソースは、SELECT TRANSFORM を使用して呼び出されます。
- Java スクリプトファイルをコンパイルし、.jarパッケージとしてエクスポートします。 この例では、.jar パッケージの名前は Sum.jar です。
package com.aliyun.odps.test; import java.util.Scanner public class Sum { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (sc.hasNext()) { String s = sc.nextLine(); String[] tokens = s.split("\t"); if (tokens.length < 2) { throw new RuntimeException("illegal input"); } if (tokens[0].equals("\\N") || tokens[1].equals("\\N")) { System.out.println("\\N"); } System.out.println(Long.parseLong(tokens[0]) + Long.parseLong(tokens[1])); } } }
- リソースとして .jar パッケージを MaxCompute に追加します。
add jar ./Sum.jar -f;
- SELECT TRANSFORM を使用して、リソースを呼び出します。
Create table testdata(c1 bigint,c2 bigint); - Creates a test table. insert into Table testdata values (1,4),(2,5),(3,6); - Inserts test data into the test table. - Execute the SELECT TRANSFORM statement: SELECT TRANSFORM(testdata.c1, testdata.c2) USING 'java -cp Sum.jar com.aliyun.odps.test.Sum' resources 'Sum.jar' from testdata; - Or: set odps.sql.session.resources=Sum.jar; SELECT TRANSFORM(testdata.c1, testdata.c2) USING 'java -cp Sum.jar com.aliyun.odps.test.Sum' FROM testdata;
+-----+
| cnt |
+-----+
| 5 |
| 7 |
| 9 |
+-----+
他の言語のスクリプトを呼び出す
上記の言語拡張に加えて、SELECT TRANSFORM は、AWK や Perl などの一般的に使用される Unix コマンドおよびスクリプトインタープリターをサポートしています。
SELECT TRANSFORM(*) USING "awk '//{print $2}'" as (data) from testdata;
SELECT TRANSFORM (testdata.c1, testdata.c2) USING "perl -e 'while($input = <STDIN>){print $input;}'" FROM testdata;
並行してスクリプトを呼び出す
SELECT TRANSFORM(key, value) USING 'cmd2' from
(
SELECT TRANSFORM(*) USINg 'cmd1' from
(
SELECt * FROM data distribute by col2 sort by col1
) t distribute by key sort by value
) t2;
@a := select * from data distribute by col2 sort by col1;
@b := map * using 'cmd1' distribute by col1 sort by col2 from @a;
reduce * using 'cmd2' from @b;
SELECT TRANSFORM のパフォーマンス
SELECT TRANSFORM および UDTF のパフォーマンスは、シナリオによって異なります。 一般に、SELECT TRANSFORM のパフォーマンスはデータサイズが比較的小さいときに最適であり、UDTF のパフォーマンスはデータサイズが大きいときに最適です。
SELECT TRANSFORM は使いやすいため、アドホックデータ分析に適しています。
- UDTF では、出力と入力は指定されたデータ型に従う必要があるため、UDTF は SELECT TRANSFORM で必要になるデータ型変換を必要としません。
- UDTF では、SELECT TRANSFORM と同様に、オペレーティングシステムパイプが空であるか、完全に使用されている場合でもプロセスは中断されません。
- UDTF では、定数パラメーターを送信する必要はありません (SELECT TRANSFORM では必要です)。
- SELECT TRANSFORM は子プロセスと親プロセスをサポートしているため、サーバーの複数のコアを活用することができます。
- SELECT TRANSFORM は、基礎となるシステムを呼び出して、送信するデータを読み書きします。これにより、Java よりも高いパフォーマンスを実現できます。
- SELECT TRANSFORM は、AWK などのツールをサポートし、ネイティブコードを実行することができます。 このため、SELECT TRANSFORM には Java と比較してより多くの利点があります。