O MaxCompute V2.0 aplica regras de sintaxe SQL mais rigorosas para alinhar-se aos ecossistemas open-source e melhorar o desempenho. Algumas instruções executadas sem erros na V1.0 agora falham ou geram avisos na V2.0.
Este tópico aborda cada categoria de incompatibilidade, descreve o erro ou a mudança de comportamento e mostra como reescrever as instruções afetadas.
Antes de começar
Se o MaxCompute V2.0 não conseguir executar um job, o framework reverterá automaticamente para a V1.0. Essa reversão aumenta a latência do job. Para evitar que a reversão mascare problemas durante a migração, desative-a antes de enviar jobs:
set odps.sql.planner.mode=lot;
A equipe do MaxCompute notifica os proprietários dos jobs por e-mail ou DingTalk ao detectar reversões. Corrija as instruções afetadas o mais rápido possível para evitar falhas nos jobs após a remoção do suporte à reversão.
Referência rápida
|
Categoria |
Descrição |
|
|
|
|
Sequências de escape devem usar exatamente três dígitos octais |
|
|
Nomes de coluna duplicados em CREATE TABLE |
|
|
Nomes de chave de partição duplicados em uma consulta |
|
|
Conversão implícita de STRING para DOUBLE em condições JOIN |
|
|
Funções de janela não podem referenciar aliases de funções de janela irmãs |
|
|
|
|
|
HAVING não pode usar aliases da cláusula SELECT |
|
|
ORDER BY exige cláusula LIMIT |
|
|
Aliases de coluna gerados automaticamente podem mudar entre versões |
|
|
Expressões não BOOLEAN em filtros WHERE/HAVING |
|
|
Referências de coluna ambíguas em ORDER BY / CLUSTER BY / DISTRIBUTE BY / SORT BY |
|
|
ORDER BY referencia aliases duplicados em SELECT |
|
|
Coluna na subconsulta IN não existe na tabela source |
|
|
Validação atual de erros de sintaxe na tabela de destino CTAS |
|
|
Otimizador pode converter partições dinâmicas em estáticas |
|
|
Tratamento de NULL em subconsultas NOT IN |
|
|
HAVING não pode usar aliases de coluna definidos em SELECT |
|
|
Constant folding de divisão causa erros para |
|
|
Hints MAPJOIN substituem a otimização de multi-way join |
|
|
Tabelas pequenas grandes em MAPJOIN causam erros de falta de memória no worker |
|
|
Primeiro argumento de WM_CONCAT deve ser constante |
|
|
Falha na conversão implícita de tipo em comparações de chave de partição |
|
|
Execução vetorizada de UDF causa timeouts de heartbeat |
Instruções DDL
group.by.with.star
Na V1.0, SELECT * FROM t GROUP BY key era válido mesmo sem listar todas as colunas na cláusula GROUP BY. Na V2.0, cada coluna da tabela source deve aparecer na cláusula GROUP BY.
V1.0 (falha na V2.0):
select * from t group by key;
Erro:
FAILED: ODPS-0130071:[1,8] Semantic analysis exception - column reference t.value should appear in GROUP BY key
V2.0:
select distinct key from t;
Se a cláusula GROUP BY incluir todas as colunas, a instrução não gera erro na V2.0, mas a forma SELECT DISTINCT é mais clara:
-- Not recommended (no error, but intent is unclear)
select * from t group by key, value; -- t has columns key and value
-- Recommended
select distinct key, value from t;
column.repeated.in.creation
A V2.0 retorna erro quando uma instrução CREATE TABLE define o mesmo nome de coluna mais de uma vez. A V1.0 permitia essa definição.
V1.0 (falha na V2.0):
create table t (a BIGINT, b BIGINT, a BIGINT);
Erro:
FAILED: ODPS-0130071:[1,37] Semantic analysis exception - column repeated in creation: a
V2.0:
create table t (a BIGINT, b BIGINT);
duplicated.partition.column
Na V1.0, especificar a mesma chave de partição duas vezes mantinha silenciosamente o último valor. A V2.0 retorna erro.
V1.0 — valor de partição duplicado (falha na V2.0):
insert overwrite table t partition (ds = '1', ds = '2') select ...;
-- In V1.0, ds = '1' was silently ignored
V2.0:
insert overwrite table t partition (ds = '2') select ...;
V1.0 — coluna aparece tanto na definição da tabela quanto na partição (falha na V2.0):
create table t (a bigint, ds string) partitioned by (ds string);
V2.0:
create table t (a bigint) partitioned by (ds string);
ctas.if.not.exists
Na V1.0, se a tabela de destino já existisse, CREATE TABLE IF NOT EXISTS ... AS SELECT ignorava a validação de sintaxe. A V2.0 sempre valida a consulta; portanto, erros anteriormente ocultos agora aparecem.
V1.0 (falha na V2.0):
create table if not exists table_name
as
select * from not_exist_table;
Erro:
FAILED: ODPS-0130131:[1,50] Table not found - table meta_dev.not_exist_table cannot be resolved
Corrija a consulta referenciando uma tabela existente.
dynamic.pt.to.static
O otimizador da V2.0 pode converter partição dinâmica em estática durante o planejamento. Se o valor de partição inferido for inválido (por exemplo, variável não resolvida como '${bizdate}'), a V2.0 retorna erro durante a validação de sintaxe.
Exemplo de conversão:
-- Input
insert overwrite table srcpt partition(pt) select id, 'pt1' from table_name;
-- V2.0 converts this to:
insert overwrite table srcpt partition(pt='pt1') select id from table_name;
V1.0 (falha na V2.0 quando o valor da partição é inválido):
insert overwrite table srcpt partition(pt) select id, '${bizdate}' from table_name limit 0;
Erro:
FAILED: ODPS-0130071:[1,24] Semantic analysis exception - wrong columns count 2 in data source, requires 3 columns (includes dynamic partitions if any)
Na V1.0, LIMIT 0 não retornava linhas; logo, nenhuma partição era criada e nenhum erro ocorria. Na V2.0, o otimizador avalia a instrução antes da execução, o que aciona o erro.
Para mais informações, consulte o tópico Partition na documentação do MaxCompute.
Conversões de tipo
bad.escape
Em literais de string, escreva cada caractere ASCII (0–127) como barra invertida seguida por exatamente três dígitos octais (por exemplo, \001 para 0, \002 para 1). A V1.0 aceitava silenciosamente sequências fora do padrão, como \01 e \0001, tratando ambas como \001. A V2.0 as rejeita.
V1.0 (falha na V2.0):
select split(key, "\01"), value like "\0001" from t;
Erro:
FAILED: ODPS-0130161:[1,19] Parse exception - unexpected escape sequence: 01
ODPS-0130161:[1,38] Parse exception - unexpected escape sequence: 0001
V2.0:
select split(key, "\001"), value like "\001" from t;
Adicionar dígitos a\000(como\0001até\0009, ou\00001) também aciona este erro.
string.join.double
Ao unir colunas STRING e DOUBLE, a V1.0 convertia ambas para BIGINT, causando perda de precisão (por exemplo, 1.1 = "1" era avaliado como igual). Seguindo a compatibilidade com Apache Hive, a V2.0 converte ambas para DOUBLE e emite aviso.
V1.0 (aciona aviso na V2.0):
select * from t1 join t2 on t1.double_value = t2.string_value;
WARNING:[1,48] implicit conversion from STRING to DOUBLE, potential data loss, use CAST function to suppress
V2.0:
select * from t1 join t2 on t1.double_value = cast(t2.string_value as double);
pt.implicit.convertion.failed
Ao filtrar coluna de partição STRING com constantes INT, a V1.0 convertia silenciosamente as constantes para DOUBLE para comparação, filtrando todos os valores de partição. A V2.0 retorna erro.
Considere a seguinte tabela:
create table srcpt(key STRING, value STRING) partitioned by (pt STRING);
alter table srcpt add partition (pt='pt1');
alter table srcpt add partition (pt='pt2');
V1.0 (falha na V2.0):
select key from srcpt where pt in (1, 2);
Erro:
FAILED: ODPS-0130071:[0,0] Semantic analysis exception - physical plan generation failed:
java.lang.NumberFormatException: ODPS-0123091:Illegal type cast -
In function cast, value 'pt1' cannot be casted from String to Double.
V2.0:
select key from srcpt where pt in ('1', '2');
Não compare colunas de partição STRING com constantes INT. Em vez disso, converta as constantes INT para STRING.
Mecanismo de consulta
window.ref.prev.window.alias
Função de janela não pode referenciar alias de outra função de janela na mesma cláusula SELECT. A V1.0 permitia isso em alguns casos; a V2.0 retorna erro.
V1.0 (falha na V2.0):
select row_number() over (partition by c1 order by c1) rn,
row_number() over (partition by c1 order by rn) rn2
from t1;
-- rn does not exist in t1
Erro:
FAILED: ODPS-0130071:[2,45] Semantic analysis exception - column rn cannot be resolved
V2.0:
select row_number() over (partition by c1 order by rn) rn2
from (
select c1, row_number() over (partition by c1 order by c1) rn
from t1
) tmp;
select.invalid.token.after.star
SELECT * não aceita alias subsequente, mesmo quando * se expande para uma única coluna.
V1.0 (falha na V2.0):
select * as alias from table_test;
Erro:
FAILED: ODPS-0130161:[1,10] Parse exception - invalid token 'as'
V2.0:
select * from table_test;
agg.having.ref.select.alias
A avaliação da cláusula HAVING ocorre antes da cláusula SELECT. Aliases de coluna definidos em SELECT não estão disponíveis em HAVING.
V1.0 (falha na V2.0):
select count(c1) cnt,
sum(c1) / cnt avg
from t1
group by c2
having cnt > 1;
-- cnt is a SELECT alias; it does not exist in source table t1
Erro:
FAILED: ODPS-0130071:[2,11] Semantic analysis exception - column cnt cannot be resolved
ODPS-0130071:[2,11] Semantic analysis exception - column reference cnt should appear in GROUP BY key
V2.0:
select cnt, s, s/cnt avg
from (
select count(c1) cnt,
sum(c1) s
from t1
group by c2
having count(c1) > 1
) tmp;
having.use.select.alias
Conforme a especificação SQL, a avaliação de GROUP BY e HAVING precede SELECT; portanto, aliases da cláusula SELECT não são visíveis em HAVING.
V1.0 (falha na V2.0):
select id id2 from table_name group by id having id2 > 0;
Erro:
FAILED: ODPS-0130071:[1,44] Semantic analysis exception - column id2 cannot be resolved
ODPS-0130071:[1,44] Semantic analysis exception - column reference id2 should appear in GROUP BY key
id2 é alias da cláusula SELECT e não pode ser usado em HAVING.
V2.0:
select id id2 from table_name group by id having id > 0;
order.by.no.limit
No MaxCompute, ORDER BY classifica todas as linhas do conjunto de resultados, operação custosa para grandes volumes de dados. A V2.0 exige cláusula LIMIT acompanhando cada cláusula ORDER BY.
V1.0 (falha na V2.0):
select * from (
select *
from (
select cast(login_user_cnt as int) as uv, '3' as shuzi
from test_login_cnt where type = 'device' and type_name = 'mobile'
) v
order by v.uv desc -- missing LIMIT here
) v
order by v.shuzi limit 20;
Erro:
FAILED: ODPS-0130071:[4,1] Semantic analysis exception - ORDER BY must be used with a LIMIT clause
V2.0: Adicione cláusula LIMIT à subconsulta:
select * from (
select *
from (
select cast(login_user_cnt as int) as uv, '3' as shuzi
from test_login_cnt where type = 'device' and type_name = 'mobile'
) v
order by v.uv desc limit 1000 -- example value; replace with a suitable limit for your data
) v
order by v.shuzi limit 20;
A V1.0 não aplicava essa regra para views criadas em projetos com odps.sql.validate.orderby.limit=false. Por exemplo, esta criação de view tinha sucesso na V1.0:
create view table_view as select id from table_view order by id;
Consultar essa view na V2.0 retorna:
FAILED: ODPS-0130071:[1,15] Semantic analysis exception - while resolving view xdj.xdj_view_limit - ORDER BY must be used with a LIMIT clause
Adicione cláusula LIMIT ao ORDER BY dentro da definição da view.
generated.column.name.multi.window
A V1.0 gerava automaticamente aliases para expressões em instruções SELECT. Esses aliases eram instáveis e podiam mudar entre versões do MaxCompute ou durante atualizações e reversões. A V2.0 alerta contra a dependência de aliases gerados automaticamente.
V1.0 (não recomendado):
select _c0 from (select count(*) from table_name) t;
-- _c0 is an auto-generated alias that may change
V2.0:
select c from (select count(*) c from table_name) t;
Sempre especifique aliases explícitos para colunas computadas.
non.boolean.filter
O MaxCompute não suporta conversões implícitas entre BOOLEAN e outros tipos. A V1.0 permitia valores BIGINT como condições de filtro em alguns casos; a V2.0 não permite.
V1.0 (falha na V2.0):
select id, count(*) from table_name group by id having id;
Erro:
FAILED: ODPS-0130071:[1,50] Semantic analysis exception - expect a BOOLEAN expression
V2.0:
select id, count(*) from table_name group by id having id <> 0;
post.select.ambiguous
Quando ORDER BY, CLUSTER BY, DISTRIBUTE BY ou SORT BY referencia nome que aparece tanto como coluna source quanto como alias da cláusula SELECT, a V1.0 selecionava silenciosamente a última coluna na lista SELECT. A V2.0 retorna erro.
V1.0 (falha na V2.0):
select a, b as a from t order by a limit 10;
-- a is ambiguous: it could be t.a or the alias for b
Erro:
FAILED: ODPS-0130071:[1,34] Semantic analysis exception - a is ambiguous, can be both t.a or null.a
V2.0:
select a as c, b as a from t order by a limit 10;
Esse erro também se aplica quando os nomes conflitantes referem-se à mesma coluna. Atualize instruções com nomes de coluna conflitantes para usar aliases inequívocos.
order.by.col.ambiguous
Quando ORDER BY referencia nome que aparece como alias duplicado na cláusula SELECT, a V2.0 retorna erro.
V1.0 (falha na V2.0):
select id, id
from table_test
order by id;
V2.0:
select id, id id2
from table_name
order by id;
Remova o alias duplicado antes de usar ORDER BY.
in.subquery.without.result
Se coluna referenciada em subconsulta IN não existir na tabela source, a V2.0 retorna erro de resolução de coluna mesmo que a subconsulta não retorne linhas.
V1.0 (falha na V2.0):
select * from table_name
where not_exist_col in (select id from table_name limit 0);
Erro:
FAILED: ODPS-0130071:[2,7] Semantic analysis exception - column not_exist_col cannot be resolved
Corrija o nome da coluna na cláusula WHERE.
lot.not.in.subquery
A V2.0 segue a semântica padrão de NULL do SQL para subconsultas IN e NOT IN:
1 IN (NULL, 1, 2, 3)retornaTRUE1 IN (NULL, 2, 3)retornaNULLNULL IN (NULL, 1, 2, 3)retornaNULL
Para NOT IN, se a subconsulta retornar qualquer valor NULL, o resultado será NULL (não TRUE) para cada linha não correspondente. A V1.0 retornava TRUE nesse caso.
Exemplo:
select * from t where c not in (select accepted from c_list);
Se accepted contiver valores NULL, esta consulta retornará resultados diferentes na V1.0 e na V2.0. Na V1.0, linhas onde c não corresponde a nenhum valor não NULL em accepted são retornadas. Na V2.0, a consulta não retorna linhas se accepted contiver quaisquer valores NULL.
V2.0 (exclua valores NULL explicitamente):
select * from t where c not in (select accepted from c_list where accepted is not null);
Verifique subconsultas usadas com NOT IN para confirmar se valores NULL no resultado da subconsulta correspondem ao comportamento desejado.
Constant folding
divide.nan.or.overflow
A V1.0 não avaliava subexpressões constantes durante o planejamento. Por exemplo, em IF(FALSE, 0/0, 1.0), a divisão nunca era executada porque a condição era falsa. A V2.0 realiza constant folding durante o planejamento, avaliando todas as subexpressões constantes — inclusive aquelas em ramificações que nunca serão executadas — e retorna erro se alguma produzir NaN ou overflow.
V1.0 (falha na V2.0):
select IF(FALSE, 0/0, 1.0) from table_name;
Erro (NaN):
FAILED: ODPS-0130071:[1,19] Semantic analysis exception - encounter runtime exception while evaluating function /,
detailed message: DIVIDE func result NaN, two params are 0.000000 and 0.000000
V1.0 (overflow):
select if(false, 1/0, 1.0) from table_name;
Erro (overflow):
FAILED: ODPS-0130071:[1,19] Semantic analysis exception - encounter runtime exception while evaluating function /,
detailed message: DIVIDE func result overflow, two params are 1.000000 and 0.000000
V2.0: Remova todas as expressões /0 e substitua-as por constantes válidas.
O mesmo problema se aplica a CASE WHEN. O otimizador move a divisão para subconsultas, onde o constant folding então a avalia:
V1.0:
select case when key = 0 then 0 else 1/key end
from (
select 0 as key from src
union all
select key from src
) r;
Erro:
FAILED: ODPS-0130071:[0,0] Semantic analysis exception - physical plan generation failed:
java.lang.ArithmeticException: DIVIDE func result overflow, two params are 1.000000 and 0.000000
V2.0: Mova a lógica CASE WHEN para uma subconsulta e elimine a ramificação /0:
select c1
from (
select 0 c1 from src
union all
select case when key = 0 then 0 else 1/key end c1 from src
) r;
Memória e UDFs
small.table.exceeds.mem.limit
Na V1.0, a otimização de multi-way join mesclava várias operações JOIN que compartilhavam a mesma chave de junção em uma única tarefa Fuxi, e hints MAPJOIN eram ignorados quando essa otimização se aplicava.
A V2.0 respeita hints MAPJOIN e os aplica primeiro. Se a tabela indicada for grande, o MAPJOIN excede o limite de memória de 640 MB e falha.
V1.0 (falha na V2.0):
select /* +mapjoin(t1) */ t1.*
from t1 join t2 on t1.c1 = t2.c1
join t3 on t1.c1 = t3.c1;
Erro:
FAILED: ODPS-0010000:System internal error - SQL Runtime Internal Error:
Hash Join Cursor HashJoin_REL… small table exceeds, memory limit(MB) 640,
fixed memory used …, variable memory used …
V2.0: Remova o hint MAPJOIN e permita que a V2.0 use a otimização de multi-way join:
select t1.*
from t1 join t2 on t1.c1 = t2.c1
join t3 on t1.c1 = t3.c1;
sigkill.oom
Este problema tem a mesma causa raiz que small.table.exceeds.mem.limit. Quando hints MAPJOIN estão presentes e as tabelas pequenas são grandes, a V1.0 revertia silenciosamente para a otimização de multi-way join. Na V2.0, o MAPJOIN é aplicado e o processo worker é encerrado ao exceder o limite de memória — mesmo que odps.sql.mapjoin.memory.max esteja definido.
Erro:
Fuxi job failed - WorkerRestart errCode:9,errMsg:SigKill(OOM), usually caused by OOM(out of memory).
V2.0: Remova hints MAPJOIN e confie na otimização de multi-way join.
wm_concat.first.argument.const
A assinatura da função WM_CONCAT exige que seu primeiro argumento (o separador) seja uma constante STRING:
string wm_concat(string separator, string str)
Na V1.0, essa restrição não era aplicada se a tabela source estivesse vazia. A V2.0 valida todos os argumentos durante o planejamento; portanto, um primeiro argumento não constante sempre falha.
V1.0 (falha na V2.0):
select wm_concat(value, ',') FROM src group by value;
-- value is a column reference, not a constant
Erro:
FAILED: ODPS-0130071:[0,0] Semantic analysis exception - physical plan generation failed:
com.aliyun.odps.lot.cbo.validator.AggregateCallValidator$AggregateCallValidationException:
Invalid argument type - The first argument of WM_CONCAT must be constant string.
V2.0:
select wm_concat(',', value) FROM src group by value;
Para mais informações, consulte o tópico Aggregate functions na Referência SQL do MaxCompute.
worker.restart.instance.timeout
Na V1.0, cada registro produzido por User-Defined Function (UDF) acionava gravação no Apsara Distributed File System e enviava heartbeat ao Job Scheduler. Se a UDF não produzisse registros por 10 minutos, o job atingia timeout.
A V2.0 usa execução vetorizada, processando múltiplas linhas de uma coluna por vez. Quando um lote de linhas leva mais de 10 minutos para produzir saída, o timeout de heartbeat é acionado mesmo que a UDF funcione corretamente.
Erro:
FAILED: ODPS-0123144: Fuxi job failed - WorkerRestart errCode:252,errMsg:kInstanceMonitorTimeout,
usually caused by bad udf performance.
Primeiro, analise o perfil da UDF. Se o processamento de cada registro levar vários segundos, otimize a lógica da UDF.
Se não for possível otimizar ainda mais a UDF, reduza o tamanho do lote. O tamanho padrão do lote é 1.024 linhas:
set odps.sql.executionengine.batch.rowcount=16;
Lotes menores são concluídos mais rapidamente e enviam heartbeats com maior frequência, evitando o timeout.