sqlpostgresqlstringsindexing

REVERSE no PostgreSQL: inverter strings, busca por sufixo e palindromos

Como REVERSE inverte uma string caractere a caractere, habilita busca por sufixo com indice, verifica palindromos e lida com multibyte.

3 min de leituraReferencesql · postgresql · strings · indexing · mysql

REVERSE e uma funcao escalar de string que retorna um texto lido de tras para frente. Parece um brinquedo, mas e justamente o que transforma uma busca "termina com" em algo indexavel, verifica palindromos em uma unica expressao e inverte listas com elegancia.

Sintaxe e exemplo basico

A assinatura e simples ao maximo: REVERSE(string). Ela trabalha caractere a caractere e retorna texto do mesmo tipo.

SELECT REVERSE('postgres') AS flipped;
-- 'sergtsop'

SELECT id, name, REVERSE(name) AS name_rev
FROM users;

Alguns comportamentos que vale guardar:

  • REVERSE opera sobre caracteres, nao sobre bytes, e inverte corretamente os caracteres multibyte UTF-8.
  • NULL na entrada produz NULL na saida, entao nenhum tratamento especial e necessario.
  • O resultado e deterministico e independente da collation: o que e reordenado sao os caracteres.

Um indice por sufixo para buscas "termina com"

Uma dor classica: WHERE email LIKE '%@gmail.com' nao consegue usar um indice B-tree comum, porque o padrao comeca com %. O truque e inverter a string para que o "sufixo" vire "prefixo", e a busca por prefixo e indexavel.

CREATE INDEX idx_users_email_rev
ON users (REVERSE(email) text_pattern_ops);

-- ends-with becomes a prefix scan on the reversed value
SELECT id, email
FROM users
WHERE REVERSE(email) LIKE REVERSE('%@gmail.com');

Apos o REVERSE, o padrao '%@gmail.com' vira 'moc.liamg@%', um prefixo ancorado moc.liamg@, de modo que o planejador pode percorrer o indice de expressao.

  • A classe de operadores text_pattern_ops e obrigatoria para prefixos LIKE fora da locale C.
  • A expressao do WHERE precisa coincidir literalmente com a do indice, ou o indice nao sera usado.
  • Uma alternativa sem REVERSE e email LIKE '%@gmail.com' mais a extensao pg_trgm com um indice GIN, mas para um "termina com" estrito o prefixo invertido e mais preciso e leve.

Pegadinha: um indice sobre REVERSE(email) so acelera buscas por final. Ele nao cobre uma consulta comum email LIKE 'john%' (comeca com), que precisa do proprio indice sobre a coluna original.

Verificacao de palindromos

Um palindromo e uma string igual ao seu reflexo. Com REVERSE isso e uma unica expressao, sem procedimentos nem lacos.

SELECT name
FROM users
WHERE LOWER(name) = REVERSE(LOWER(name));

LOWER aqui remove a distincao de maiusculas, entao Anna conta como palindromo. Para ignorar espacos e pontuacao, normalize a string primeiro:

SELECT name,
       regexp_replace(LOWER(name), '[^a-z0-9]', '', 'g') AS norm
FROM users
WHERE regexp_replace(LOWER(name), '[^a-z0-9]', '', 'g')
    = REVERSE(regexp_replace(LOWER(name), '[^a-z0-9]', '', 'g'));

Inverter uma lista com delimitador

REVERSE inverte caracteres, nao elementos. Para transformar a lista 'a,b,c' em 'c,b,a', divida, inverta a ordem e junte de volta. No PostgreSQL o caminho legivel e string_to_array mais unnest ordenado de forma decrescente e depois string_agg.

-- reverse the ELEMENTS of a delimited list, not the characters
SELECT string_agg(part, ',') AS reversed_list
FROM (
    SELECT part, ord
    FROM unnest(string_to_array('mon,tue,wed', ',')) WITH ORDINALITY AS t(part, ord)
    ORDER BY ord DESC
) s;
-- 'wed,tue,mon'

Quando voce so precisa de um elemento a partir do final, como o dominio de topo de a.b.com, combine REVERSE com SPLIT_PART:

-- last segment of a dotted host, taken as the first segment after REVERSE
SELECT REVERSE(SPLIT_PART(REVERSE('mail.corp.example.com'), '.', 1)) AS tld;
-- 'com'

Aqui invertemos a string, pegamos o primeiro segmento (o ultimo no original) e o invertemos de volta. E o movimento canonico de "indexar a partir do final".

Multibyte e diferencas entre motores

REVERSE no PostgreSQL e seguro com Unicode: opera sobre pontos de codigo, nao sobre bytes. Mas ha nuances comuns a todos os motores:

  • Grafemas compostos (uma letra mais um acento combinante, ou um emoji com modificadores) podem quebrar ao serem invertidos, porque pontos de codigo sao reordenados, nao grafemas. Para texto com diacriticos essa e uma armadilha rara, mas real.
  • No MySQL a funcao tambem e REVERSE e e segura com multibyte sob um charset UTF-8; garanta que a coluna seja utf8mb4, ou os emojis sao corrompidos antes mesmo da inversao.
  • No ClickHouse existem um reverse em nivel de byte e um reverseUTF8 - para strings nao ASCII use reverseUTF8, ou voce obtem uma sequencia de bytes quebrada.

Quando voce precisa de um "termina com" indexavel, uma verificacao de palindromo previsivel ou acesso a um elemento a partir do final, REVERSE continua sendo a ferramenta mais direta - apenas lembre que ela reordena caracteres, nao grafemas nem elementos de lista.

Pratique com exercícios reais

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

Abrir o treinador