sqlpostgresqlstringsindexing

REVERSE en PostgreSQL: invertir cadenas, busqueda por sufijo y palindromos

Como REVERSE invierte una cadena caracter a caracter, habilita la busqueda por sufijo con indice, comprueba palindromos y maneja el multibyte.

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

REVERSE es una funcion escalar de cadena que devuelve un texto leido de atras hacia adelante. Parece un juguete, pero es justo lo que convierte una busqueda "termina en" en una indexable, comprueba palindromos en una sola expresion e invierte listas con limpieza.

Sintaxis y ejemplo basico

La firma es de lo mas simple: REVERSE(string). Trabaja caracter a caracter y devuelve texto del mismo tipo.

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

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

Algunos comportamientos que conviene recordar:

  • REVERSE opera sobre caracteres, no sobre bytes, e invierte correctamente los caracteres multibyte UTF-8.
  • NULL de entrada produce NULL de salida, asi que no hace falta tratamiento especial.
  • El resultado es determinista e independiente de la collation: lo que se reordena son los caracteres.

Un indice por sufijo para busquedas "termina en"

Un dolor clasico: WHERE email LIKE '%@gmail.com' no puede usar un indice B-tree normal, porque el patron empieza por %. El truco es invertir la cadena para que el "sufijo" se vuelva "prefijo", y la busqueda por prefijo si es indexable.

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');

Tras REVERSE, el patron '%@gmail.com' se convierte en 'moc.liamg@%', un prefijo anclado moc.liamg@, de modo que el planificador puede recorrer el indice de expresion.

  • La clase de operadores text_pattern_ops es obligatoria para prefijos LIKE fuera de la locale C.
  • La expresion del WHERE debe coincidir literalmente con la del indice, o no se usara el indice.
  • Una alternativa sin REVERSE es email LIKE '%@gmail.com' mas la extension pg_trgm con un indice GIN, pero para un "termina en" estricto el prefijo invertido es mas preciso y ligero.

Trampa: un indice sobre REVERSE(email) solo acelera las busquedas por final. No cubre una consulta normal email LIKE 'john%' (empieza por), que necesita su propio indice sobre la columna original.

Comprobacion de palindromos

Un palindromo es una cadena igual a su reflejo. Con REVERSE eso es una sola expresion, sin procedimientos ni bucles.

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

LOWER aqui elimina la distincion de mayusculas, asi que Anna cuenta como palindromo. Para ignorar espacios y signos, normaliza la cadena primero:

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'));

Invertir una lista con delimitador

REVERSE invierte caracteres, no elementos. Para convertir la lista 'a,b,c' en 'c,b,a', divide, invierte el orden y vuelve a unir. En PostgreSQL la via legible es string_to_array mas unnest ordenado de forma descendente y luego 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'

Cuando solo necesitas un elemento desde el final, como el dominio de nivel superior de a.b.com, combina REVERSE con 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 invertimos la cadena, tomamos el primer segmento (el ultimo en el original) y lo volvemos a invertir. Es el movimiento canonico de "indexar desde el final".

Multibyte y diferencias entre motores

REVERSE en PostgreSQL es seguro con Unicode: opera sobre puntos de codigo, no sobre bytes. Pero hay matices comunes a todos los motores:

  • Los grafemas compuestos (una letra mas un acento combinable, o un emoji con modificadores) pueden romperse al invertir, porque se reordenan puntos de codigo, no grafemas. Para texto con diacriticos es una trampa rara pero real.
  • En MySQL la funcion tambien es REVERSE y es segura con multibyte bajo un charset UTF-8; asegurate de que la columna sea utf8mb4, o los emojis se estropean antes incluso de invertirlos.
  • En ClickHouse existen un reverse a nivel de byte y un reverseUTF8 - para cadenas no ASCII usa reverseUTF8, o obtendras una secuencia de bytes rota.

Cuando necesitas un "termina en" indexable, una comprobacion de palindromo predecible o acceder a un elemento desde el final, REVERSE sigue siendo la herramienta mas directa - solo recuerda que reordena caracteres, no grafemas ni elementos de lista.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador