A instrução SELECT TRANSFORM inicia um processo filho específico e utiliza a entrada padrão para inserir dados no formato necessário. Em seguida, é possível analisar a saída padrão desse processo filho para obter os dados de resultado. A instrução SELECT TRANSFORM permite executar scripts em outras linguagens de programação sem precisar escrever funções com valor de tabela definidas pelo usuário (UDTFs).
Descrição
O desempenho do SELECT TRANSFORM e das UDTFs varia conforme o cenário. Testes comparativos indicam que o SELECT TRANSFORM apresenta melhor desempenho na consulta de pequenos volumes de dados. Por outro lado, as UDTFs superam o SELECT TRANSFORM em consultas de grandes volumes de dados. O SELECT TRANSFORM oferece desenvolvimento simplificado e é mais adequado para consultas ad hoc.
O SELECT TRANSFORM é compatível com diversas linguagens de programação. Essa instrução permite escrever scripts diretamente nos comandos para implementar funcionalidades simples. Linguagens como AWK, Python, Perl e Shell suportam essa operação. Assim, não é necessário executar etapas adicionais, como criar arquivos de script e fazer upload de resources, o que simplifica o processo de desenvolvimento. Para implementar funcionalidades complexas, faça o upload dos arquivos de script. Para mais informações, consulte {{XREF_0}} e {{XREF_1}}.
A tabela a seguir apresenta os resultados da comparação entre UDTFs e SELECT TRANSFORM em diferentes dimensões.
|
Categoria |
select transform |
UDTF |
|
Tipo de dados |
Um processo filho usa entrada e saída padrão para transmitir dados. Todos os dados são processados como strings. Ao usar o |
Os resultados de saída e os parâmetros de entrada das UDTFs suportam múltiplos tipos de dados. |
|
Transmissão de dados |
A transmissão baseia-se no pipeline do sistema operacional. No entanto, o tamanho do cache do pipeline é de apenas 4 KB e não pode ser alterado. Se o pipeline estiver vazio ou totalmente ocupado, o O SELECT TRANSFORM chama sistemas subjacentes para ler e gravar dados durante a transmissão. Essa instrução oferece desempenho superior na transmissão de dados em comparação com programas Java. |
Não há limite imposto ao cache do pipeline. |
|
Transmissão de parâmetros constantes |
É necessária a transmissão de parâmetros constantes. |
A transmissão de parâmetros constantes é opcional. |
|
Processo |
O SELECT TRANSFORM suporta processos pai e filho. Quando o uso de recursos computacionais é alto e o throughput de dados é baixo, o |
Utiliza um único processo. |
|
Desempenho |
O |
O desempenho é inferior. |
Limites
PHP e Ruby não estão implantados nos clusters de computação do MaxCompute. Portanto, não é possível chamar scripts PHP ou Ruby no MaxCompute.
Sintaxe
select transform(<arg1>, <arg2> ...)
[(row format delimited (fields terminated by <field_delimiter> (escaped by <character_escape>)) (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>)) (null defined as <null_value>))
SELECT TRANSFORM palavra-chave: obrigatória. É possível substituir essa palavra-chave por
mapoureduce, que possuem a mesma semântica. Para maior clareza sintática, recomenda-se o uso de SELECT TRANSFORM.arg1,arg2...: obrigatório. Esses parâmetros especificam os dados de entrada. Seus formatos seguem o mesmo padrão das instruções
SELECT. Caso utilize os formatos padrão, os resultados das expressões de cada parâmetro são convertidos implicitamente para valores do tipo STRING. Depois, os parâmetros são combinados com\te passados ao processo filho especificado.-
ROW FORMAT cláusula: opcional. Permite personalizar o formato dos dados de entrada e saída.
A sintaxe utiliza duas cláusulas ROW FORMAT. A primeira define o formato dos dados de entrada, enquanto a segunda define o formato dos dados de saída. Por padrão,
\tatua como delimitador de coluna,\ncomo delimitador de linha e\Nrepresenta valores nulos.NotaApenas um caractere pode ser usado como field_delimiter ou character_escape. Se uma string for especificada para esses parâmetros, somente o primeiro caractere será considerado.
O MaxCompute suporta sintaxes nos formatos definidos pelo Apache Hive, como
inputRecordReader,outputRecordReadereSerDe. Para utilizar essas sintaxes, ative a edição de tipos de dados compatível com Hive. Adicioneset odps.sql.hive.compatible=true;antes das instruções SQL. Para mais detalhes sobre as sintaxes suportadas pelo Apache Hive, consulte a documentação do Hive.Ao especificar uma sintaxe suportada pelo Apache Hive, como
inputRecordReaderououtputRecordReader, a execução das instruções SQL pode ficar mais lenta.
-
USING cláusula: obrigatória. Define o comando utilizado para iniciar o processo filho.
Na maioria das instruções SQL do MaxCompute, a cláusula USING especifica resources. Contudo, na instrução SELECT TRANSFORM, a cláusula USING define o comando para iniciar um processo filho. O uso da cláusula USING visa garantir a compatibilidade com a sintaxe do Apache Hive.
A sintaxe da cláusula USING assemelha-se à de um script shell. Entretanto, em vez de executar um script shell, a cláusula USING cria um processo filho com base no comando especificado. Logo, alguns recursos de shell, como redirecionamento de entrada e saída, pipeline e loops, não estão disponíveis. Se necessário, utilize um script shell como comando para iniciar o processo filho.
-
RESOURCES cláusula: opcional. Indica os resources acessíveis ao processo filho. Utilize um dos métodos abaixo para definir esses resources:
Utilize a cláusula RESOURCES para especificar os resources, por exemplo:
using 'sh foo.sh bar.txt' resources 'foo.sh','bar.txt'.-
Use set odps.sql.session.resources para definir os resources. Por exemplo, adicione a flag
set odps.sql.session.resources=foo.sh,bar.txt;antes das instruções SQL.Após a aplicação dessa configuração global, todas as instruções SELECT TRANSFORM poderão acessar os resources. Separe múltiplos arquivos de resource com vírgulas (,).
-
AS cláusula: opcional. Especifica as colunas de saída e seus respectivos tipos de dados, como em
as(col1 bigint, col2 boolean).Caso os tipos de dados das colunas de saída não sejam definidos, o tipo padrão STRING será aplicado. Por exemplo,
AS(col1, col2)indica que as colunas de saída são do tipo STRING.Os dados de saída resultam da análise da saída padrão do processo filho. Se os dados especificados não forem do tipo STRING, o MaxCompute chama implicitamente a função
CASTpara converter o tipo de dados para STRING. Uma exceção de tempo de execução pode ocorrer durante essa conversão.Não é permitido especificar tipos de dados apenas para algumas das colunas definidas, como em
as(col1, col2:bigint).Se a cláusula
ASfor omitida, o campo anterior ao primeiro\tnos dados de saída padrão será a chave, e todas as partes subsequentes serão o valor. Isso equivale aAS(key, value).
Exemplo de chamada de comandos shell
Execute um comando shell para gerar 50 linhas de dados, variando de 1 a 50. A saída corresponde ao campo data. Utilize a saída do comando shell como entrada para o SELECT TRANSFORM. Instrução de exemplo:
select transform(script) using 'sh' as (data)
from (
select 'for i in `seq 1 50`; do echo $i; done' as script
) t
;
-- The preceding statement is equivalent to the following statement:
select transform('for i in `seq 1 50`; do echo $i; done') using 'sh' as (data);
O seguinte resultado é retornado:
+------------+
| data |
+------------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
| 13 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
| 20 |
| 21 |
| 22 |
| 23 |
| 24 |
| 25 |
| 26 |
| 27 |
| 28 |
| 29 |
| 30 |
| 31 |
| 32 |
| 33 |
| 34 |
| 35 |
| 36 |
| 37 |
| 38 |
| 39 |
| 40 |
| 41 |
| 42 |
| 43 |
| 44 |
| 45 |
| 46 |
| 47 |
| 48 |
| 49 |
| 50 |
+------------+
Exemplo de chamada de comandos Python
Utilize um comando Python para gerar 50 linhas de dados, variando de 1 a 50. A saída corresponde ao campo data. Utilize a saída do comando Python como entrada para o SELECT TRANSFORM. Instrução de exemplo:
select transform(script) using 'python' as (data)
from (
select 'for i in xrange(1, 51): print(i);' as script
) t
;
-- The preceding statement is equivalent to the following statement:
select transform('for i in xrange(1, 51): print(i);') using 'python' as (data);
O seguinte resultado é retornado:
+------------+
| data |
+------------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
| 13 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
| 20 |
| 21 |
| 22 |
| 23 |
| 24 |
| 25 |
| 26 |
| 27 |
| 28 |
| 29 |
| 30 |
| 31 |
| 32 |
| 33 |
| 34 |
| 35 |
| 36 |
| 37 |
| 38 |
| 39 |
| 40 |
| 41 |
| 42 |
| 43 |
| 44 |
| 45 |
| 46 |
| 47 |
| 48 |
| 49 |
| 50 |
+------------+
Exemplo de chamada de comandos AWK
Crie uma tabela de teste. Execute um comando AWK para fornecer a segunda coluna da tabela de teste como saída. Os dados de saída correspondem ao campo data. Utilize a saída do comando AWK como entrada para o SELECT TRANSFORM. Instrução de exemplo:
-- Create a test table.
create table testdata(c1 bigint,c2 bigint);
-- Insert test data into the test table.
insert into table testdata values (1,4),(2,5),(3,6);
-- Execute the SELECT TRANSFORM statement.
select transform(*) using "awk '//{print $2}'" as (data) from testdata;
O seguinte resultado é retornado:
+------------+
| data |
+------------+
| 4 |
| 5 |
| 6 |
+------------+
Exemplo de chamada de comandos Perl
Crie uma tabela de teste. Execute um comando Perl para fornecer os dados da tabela de teste como saída. Os dados de saída correspondem ao campo data. Utilize a saída do comando Perl como entrada para o SELECT TRANSFORM. Instrução de exemplo:
-- Create a test table.
create table testdata(c1 bigint,c2 bigint);
-- Insert test data into the test table.
insert into table testdata values (1,4),(2,5),(3,6);
-- Execute the SELECT TRANSFORM statement.
select transform(testdata.c1, testdata.c2) using "perl -e 'while($input = <STDIN>){print $input;}'" from testdata;
O seguinte resultado é retornado:
+------------+------------+
| key | value |
+------------+------------+
| 1 | 4 |
| 2 | 5 |
| 3 | 6 |
+------------+------------+
Exemplo de chamada de scripts Python
-
Crie o arquivo myplus.py. Instruções de exemplo:
#!/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(str(token[0]) +'\t' + str(token[1])) line = sys.stdin.readline() -
Adicione o arquivo de script Python como um resource no MaxCompute.
add py ./myplus.py -f;NotaTambém é possível utilizar o console do DataWorks para adicionar o arquivo Python como resource. Para mais informações, consulte {{XREF_2}}.
-
Execute a instrução
SELECT TRANSFORMpara chamar esse arquivo.-- Create a test table. create table testdata(c1 bigint,c2 bigint); -- Insert test data into the test table. insert into table testdata values (1,4),(2,5),(3,6); -- Execute the SELECT TRANSFORM statement. select transform (testdata.c1, testdata.c2) using 'python myplus.py' resources 'myplus.py' as (result1,result2) from testdata; -- The preceding statements are equivalent to the following statements: set odps.sql.session.resources=myplus.py; select transform (testdata.c1, testdata.c2) using 'python myplus.py' as (result1,result2) from testdata;O seguinte resultado é retornado:
+------------+------------+ | result1 | result2 | +------------+------------+ | 1 | 4 | | | NULL | | 2 | 5 | | | NULL | | 3 | 6 | | | NULL | +------------+------------+
Exemplo de chamada de scripts Java
-
Escreva um script Java e exporte-o como o arquivo Sum.jar. Código Java de exemplo:
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])); } } } -
Adicione o arquivo como um resource no MaxCompute.
add jar ./Sum.jar -f; -
Execute a instrução
SELECT TRANSFORMpara chamar esse arquivo.-- Create a test table. create table testdata(c1 bigint,c2 bigint); -- Insert test data into the test table. insert into table testdata values (1,4),(2,5),(3,6); -- Execute the SELECT TRANSFORM statement. select transform(testdata.c1, testdata.c2) using 'java -cp Sum.jar com.aliyun.odps.test.Sum' resources 'Sum.jar' as cnt from testdata; -- The preceding statements are equivalent to the following statements: set odps.sql.session.resources=Sum.jar; select transform(testdata.c1, testdata.c2) using 'java -cp Sum.jar com.aliyun.odps.test.Sum' as cnt from testdata;O seguinte resultado é retornado:
+-----+ | cnt | +-----+ | 5 | | 7 | | 9 | +-----+
Java e Python possuem frameworks UDTF prontos para uso. No entanto, o SELECT TRANSFORM facilita a escrita de scripts. O SELECT TRANSFORM não exige dependências adicionais nem impõe requisitos de formato, permitindo inclusive o uso direto de scripts offline. O diretório para salvar scripts Java offline pode ser obtido pela variável de ambiente JAVA_HOME. Já o diretório para salvar scripts Python offline pode ser obtido pela variável de ambiente PYTHON_HOME.
Exemplo de chamada de scripts em série
É possível executar instruções SELECT TRANSFORM em série. Para realizar essa operação, utilize as cláusulas DISTRIBUTE BY e SORT BY para pré-processar os dados de entrada. Instrução de exemplo:
select transform(key, value) using '<cmd2>' from
(
select transform(*) using '<cmd1>' from
(
select * from testdata distribute by c2 sort by c1
) t distribute by key sort by value
) t2;
cmd1 e cmd2 são os comandos utilizados para iniciar os processos filhos.
Também é possível utilizar as palavras-chave map e reduce para executar instruções SELECT TRANSFORM em série.
@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;