sqlpostgresqlstringstranslate

TRANSLATE no SQL: mapeamento de caracteres, remocao e transliteracao

Como o TRANSLATE mapeia um conjunto de caracteres para outro, remove os excedentes e por que difere do REPLACE.

3 min de leituraReferênciasql · postgresql · strings · translate · clickhouse

O TRANSLATE recebe uma string e dois mapas de caracteres equivalentes: cada caractere do primeiro conjunto e trocado pelo da mesma posicao no segundo. E uma operacao caractere a caractere, nao de substrings, e e exatamente isso que a separa do REPLACE. Use-o quando precisar limpar ou normalizar caracteres individuais -- separadores, pontuacao, letras parecidas -- em uma unica passada, sem chamadas aninhadas nem expressoes regulares.

Sintaxe basica

A assinatura e simples: TRANSLATE(text, from_set, to_set). O caractere from_set[i] vira to_set[i].

SELECT TRANSLATE('abc', 'abc', 'xyz');  -- 'xyz'
SELECT TRANSLATE('a-b-c', 'abc', 'xyz'); -- 'x-y-z'

Os hifens ficam no lugar: nao estao no from_set, entao nao sao tocados. O TRANSLATE processa cada caractere de forma independente e em uma unica passada.

  • A correspondencia e estritamente posicional: o primeiro com o primeiro, o segundo com o segundo.
  • Caracteres fora do from_set sao copiados sem alteracao.
  • Maiusculas importam: a e A sao caracteres diferentes.

Apagar caracteres: quando to_set e mais curto

Se um caractere nao tem par no segundo conjunto, ele e removido. Esse e o uso mais pratico do TRANSLATE.

-- Normalize phone numbers: drop separators entirely
SELECT id, TRANSLATE(name, ' .,()-+', '') AS cleaned
FROM users;

Aqui o to_set esta vazio, entao cada separador listado simplesmente some. Um caso comum e tirar a formatacao de telefones ou SKUs:

-- Strip spaces, dashes and parens from a stored code
SELECT TRANSLATE('+1 (555) 123-45-67', ' ()-+', '');
-- '15551234567'

O mesmo truque arruma os status de pedido antes de um export:

SELECT id, TRANSLATE(status, '_', ' ') AS label
FROM orders;

Transliteracao em uma passada

O TRANSLATE e otimo para criar valores tipo slug ou normalizar caracteres. Mapeie cada caractere "perigoso" para um seguro em uma unica chamada:

-- Map separators and odd punctuation in one shot
SELECT id,
       TRANSLATE(LOWER(name), ' /\\.', '----') AS slug_part
FROM employees;

Leia esses literais ao pe da letra. O from_set e escrito ' /\\.': dentro de aspas simples o PostgreSQL le \\ como dois caracteres de barra invertida, entao o conjunto tem cinco caracteres -- um espaco, uma barra normal, duas barras invertidas e um ponto. O to_set '----' tem apenas quatro hifens. O espaco, a barra normal e a barra invertida recebem um hifen por posicao; a segunda barra invertida e uma repeticao e por isso e ignorada (veja o aviso abaixo), e o ponto fica sem par e e removido -- ele nao vira hifen. Se voce quiser manter o ponto como hifen, de a ele um quinto hifen no to_set; os tamanhos dos conjuntos precisam refletir sua intencao com exatidao, senao os caracteres sem par do from_set sao apagados em vez de substituidos.

Gotcha: os caracteres do from_set devem ser unicos. Se um se repete, so a primeira ocorrencia conta. TRANSLATE('a', 'aa', 'xy') resulta em 'x', nao 'y': o segundo a e ignorado.

A diferenca central para o REPLACE

O REPLACE encontra e troca uma substring inteira; o TRANSLATE trabalha caractere a caractere, sem depender da ordem.

-- REPLACE swaps a whole substring
SELECT REPLACE('a.b.c', '.', '_');     -- 'a_b_c'

-- TRANSLATE maps single chars; great for sets
SELECT TRANSLATE('a.b,c;d', '.,;', '___'); -- 'a_b_c_d'

Para remover tres separadores diferentes com REPLACE, voce precisa de tres chamadas aninhadas; o TRANSLATE faz isso com um unico conjunto.

  • Precisa trocar uma palavra ou um token de varios caracteres? Isso e REPLACE.
  • Precisa mapear ou apagar um conjunto de caracteres individuais? Isso e TRANSLATE.

Notas por motor

  • PostgreSQL: TRANSLATE(text, from, to) completo; a remocao com to_set curto funciona como descrito acima no exemplo do ponto.
  • Oracle: tambem existe, mas o to_set nao pode ser string vazia (e tratado como NULL e o resultado vira NULL). Para apagar caracteres, passe um to_set nao vazio mais curto que o from_set.
  • MySQL/MariaDB: nao ha funcao TRANSLATE. Emule com REPLACE aninhados ou REGEXP_REPLACE.
  • ClickHouse: oferece translate(s, from, to) e translateUTF8 para lidar bem com caracteres multibyte.
-- ClickHouse: byte-wise vs UTF-8 aware
SELECT translateUTF8(name, 'aeiou', 'AEIOU') FROM users;

A verdadeira armadilha do TRANSLATE nao e a troca em si, mas os casos limite onde os conjuntos se encontram: um from_set maior que o to_set (o excedente e apagado), uma duplicata no from_set (so a primeira ocorrencia conta) e os caracteres multibyte na variante por bytes. Antes de portar a logica de slug entre PostgreSQL, MySQL e ClickHouse, rode uma tabela pequena com NULL, string vazia e nao-ASCII: os motores concordam em dados limpos e divergem justamente nessas bordas, como o ponto no exemplo acima.

Lembre-se: na maioria dos motores o TRANSLATE opera sobre code points ou bytes, entao para nao-ASCII use a variante compativel com UTF-8 (translateUTF8 no ClickHouse) quando existir.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador