Introducción a las expresiones regulares

Las expresiones regulares son un lenguaje de patrones que permite describir cadenas de texto de forma flexible y poderosa. En MySQL, puedes usarlas para buscar, validar, extraer y reemplazar texto que sigue un patrón determinado. Si alguna vez has intentado buscar todos los emails de un dominio concreto, validar que un código postal tiene el formato correcto o extraer un número de teléfono de un campo de texto libre, las expresiones regulares son la herramienta que necesitas.

MySQL 8.0 introdujo un motor de expresiones regulares basado en la biblioteca ICU (International Components for Unicode), que es mucho más potente y compatible con estándares que el motor básico de versiones anteriores. Este motor soporta Unicode completo, clases de caracteres POSIX, cuantificadores, lookahead, lookbehind y muchas otras características avanzadas.

Por qué usar expresiones regulares en vez de LIKE

El operador LIKE es suficiente para búsquedas simples con comodines % y _, pero sus posibilidades son muy limitadas. Observa la diferencia entre ambos enfoques para una misma necesidad:

-- Con LIKE: buscar productos que empiecen por letra y tengan dígitos
-- No es posible expresar esto con LIKE de forma precisa
SELECT nombre FROM productos WHERE nombre LIKE '%[0-9]%';
-- Esto NO funciona como esperas: LIKE no entiende rangos de caracteres
 
-- Con REGEXP: patrón preciso
SELECT nombre FROM productos WHERE nombre REGEXP '[A-Za-z]+.*[0-9]';

Con LIKE solo puedes indicar "cualquier carácter" (_) o "cualquier secuencia" (%). No puedes especificar que buscas dígitos, letras, un número exacto de repeticiones o alternativas. Las expresiones regulares cubren todos esos casos y muchos más.

La regla general es: si puedes resolver tu búsqueda con LIKE, úsalo porque es más rápido y sencillo. Cuando necesites patrones más complejos, recurre a expresiones regulares.

Sintaxis

MySQL ofrece un operador y cuatro funciones para trabajar con expresiones regulares:

-- Operador: devuelve 1 (coincide) o 0 (no coincide)
expresion REGEXP patron
expresion RLIKE patron          -- sinónimo
 
-- Funciones (MySQL 8.0+)
REGEXP_LIKE(expresion, patron [, modo])
REGEXP_INSTR(expresion, patron [, pos [, ocurrencia [, opcion_retorno [, modo]]]])
REGEXP_REPLACE(expresion, patron, reemplazo [, pos [, ocurrencia [, modo]]])
REGEXP_SUBSTR(expresion, patron [, pos [, ocurrencia [, modo]]])

El parámetro modo permite controlar el comportamiento de la coincidencia con letras como 'c' (sensible a mayúsculas), 'i' (insensible a mayúsculas) o 'm' (modo multilínea). Lo veremos en detalle en los artículos siguientes.

Clases de caracteres

Las clases de caracteres permiten indicar un conjunto de caracteres que pueden aparecer en una posición determinada. Se escriben entre corchetes.

-- [abc] coincide con 'a', 'b' o 'c'
SELECT 'gato' REGEXP '[gc]ato' AS resultado;
resultado
1

El patrón [gc]ato busca una cadena que contenga g o c seguida de ato. Como gato empieza con g, la coincidencia es positiva.

Los rangos dentro de los corchetes simplifican la escritura de secuencias consecutivas:

-- [a-z] coincide con cualquier letra minúscula
-- [A-Z] coincide con cualquier letra mayúscula
-- [0-9] coincide con cualquier dígito
SELECT codigo FROM productos WHERE codigo REGEXP '[A-Z]{3}-[0-9]{4}';

Este patrón busca códigos con tres letras mayúsculas, un guion y cuatro dígitos, como ELE-0012 o ROD-3450.

El acento circunflejo dentro de los corchetes niega la clase:

-- [^0-9] coincide con cualquier carácter que NO sea dígito
SELECT nombre FROM clientes WHERE nombre REGEXP '[^a-zA-ZáéíóúÁÉÍÓÚñÑ ]';

Esta consulta encontraría nombres que contienen caracteres inesperados como dígitos, signos de puntuación u otros símbolos, lo cual es útil para detectar datos sucios.

MySQL también soporta secuencias de escape predefinidas que actúan como atajos de clases de caracteres comunes:

-- \d  equivale a [0-9]           (dígito)
-- \D  equivale a [^0-9]          (no dígito)
-- \w  equivale a [a-zA-Z0-9_]    (carácter de palabra)
-- \W  equivale a [^a-zA-Z0-9_]   (no carácter de palabra)
-- \s  equivale a [ \t\n\r]       (espacio en blanco)
-- \S  equivale a [^ \t\n\r]      (no espacio en blanco)
 
SELECT '12345' REGEXP '^\\d+$' AS solo_digitos;
solo_digitos
1

Observa que en MySQL debes usar doble barra invertida (\\d) porque la primera barra es el escape de la cadena SQL y la segunda es el escape de la expresión regular.

Cuantificadores

Los cuantificadores indican cuántas veces debe repetirse el elemento anterior para que haya coincidencia.

-- *    cero o más veces
-- +    una o más veces
-- ?    cero o una vez (hace el elemento opcional)
-- {n}  exactamente n veces
-- {n,} al menos n veces
-- {n,m} entre n y m veces
 
SELECT 'aabbb' REGEXP 'a{2}b{3}' AS resultado;
resultado
1

Veamos un ejemplo práctico. Supón que tienes una tabla de productos y quieres encontrar los que tienen un código de referencia con un formato específico: dos letras seguidas de tres a cinco dígitos.

SELECT nombre, referencia
FROM productos
WHERE referencia REGEXP '^[A-Z]{2}[0-9]{3,5}$';
nombrereferencia
Monitor 27 pulgadasMO275
Teclado mecánicoTC001
Ratón inalámbricoRA04520

El cuantificador {3,5} permite que la parte numérica tenga entre tres y cinco dígitos, lo que cubre tanto 275 como 04520.

El cuantificador ? hace que un elemento sea opcional. Es especialmente útil para patrones que pueden tener variantes:

-- Buscar "color" o "colour" (la 'u' es opcional)
SELECT descripcion
FROM productos
WHERE descripcion REGEXP 'colou?r';

Anclas

Las anclas no coinciden con caracteres, sino con posiciones dentro de la cadena. Son fundamentales para evitar coincidencias parciales no deseadas.

-- ^  inicio de la cadena
-- $  final de la cadena
 
-- Sin anclas: busca '123' en CUALQUIER parte
SELECT 'abc123def' REGEXP '123' AS sin_anclas;
 
-- Con anclas: exige que TODA la cadena sean dígitos
SELECT 'abc123def' REGEXP '^[0-9]+$' AS con_anclas;
sin_anclas
1
con_anclas
0

Sin anclas, el patrón 123 coincide porque esos tres caracteres existen dentro de la cadena. Con las anclas ^ y $, el patrón exige que la cadena completa sea solo dígitos, y abc123def no cumple esa condición.

Un caso práctico habitual es validar que un campo contiene exactamente un formato esperado:

-- Validar códigos postales españoles (5 dígitos, empieza por 0-5)
SELECT email, codigo_postal
FROM clientes
WHERE codigo_postal NOT REGEXP '^[0-5][0-9]{4}$';

Esta consulta devuelve los clientes cuyo código postal no tiene el formato correcto, lo cual es muy útil para limpieza de datos.

Alternancia

El operador | funciona como un "o" lógico entre patrones. Permite especificar varias alternativas:

-- Buscar productos que sean de tipo portátil, tablet o smartphone
SELECT nombre, categoria
FROM productos
WHERE categoria REGEXP 'portátil|tablet|smartphone';
nombrecategoria
MacBook Air M3portátil
iPad Pro 12.9tablet
iPhone 15 Prosmartphone
Samsung Galaxy S24smartphone
Lenovo ThinkPad X1portátil

La alternancia se evalúa de izquierda a derecha. MySQL prueba cada alternativa en orden y devuelve coincidencia con la primera que se cumple. Puedes combinar alternancia con anclas y otros elementos:

-- Buscar emails de dominios específicos
SELECT nombre, email
FROM clientes
WHERE email REGEXP '@(gmail\\.com|outlook\\.com|yahoo\\.es)$';
nombreemail
María Garcíamaria.garcia@gmail.com
Carlos Rodríguezcarlos.rodriguez@outlook.com
Laura Sánchezlaura.sanchez@yahoo.es

Observa que el punto se escapa con \\. porque el punto sin escapar coincide con cualquier carácter. Sin escaparlo, el patrón gmail.com coincidiría también con gmailXcom.

Grupos

Los paréntesis () tienen dos funciones en las expresiones regulares: agrupan elementos para aplicarles cuantificadores o alternancia, y crean referencias para usar en reemplazos.

-- Agrupar para cuantificar: buscar 'la' repetido 2 o más veces
SELECT 'lalala canción' REGEXP '(la){2,}' AS resultado;
resultado
1

Sin los paréntesis, la{2,} significaría la letra l seguida de la letra a repetida dos o más veces (es decir, laa, laaa, etc.), que no es lo mismo.

Los grupos también permiten aplicar alternancia a una parte del patrón:

-- Buscar direcciones que contengan 'Calle', 'Avenida' o 'Plaza'
SELECT direccion
FROM clientes
WHERE direccion REGEXP '^(Calle|Avenida|Plaza|Paseo) ';
direccion
Calle Gran Vía 42, 3A
Avenida de la Constitución 15
Plaza Mayor 1, bajo
Paseo de la Castellana 200

El grupo (Calle|Avenida|Plaza|Paseo) limita la alternancia a esas cuatro palabras, y el espacio después del grupo exige que haya un espacio tras el tipo de vía. El ancla ^ asegura que la dirección empiece por una de esas palabras.

Escapar caracteres especiales

Los caracteres ., *, +, ?, [, ], (, ), {, }, ^, $, | y \ tienen significado especial en las expresiones regulares. Si necesitas buscar uno de estos caracteres literalmente, debes escaparlos con una barra invertida. Como en MySQL las cadenas usan la barra invertida como carácter de escape, necesitas una doble barra:

-- Buscar precios con formato '$12.99'
SELECT descripcion
FROM productos
WHERE descripcion REGEXP '\\$[0-9]+\\.[0-9]{2}';
 
-- Buscar textos que contengan paréntesis literales
SELECT nombre
FROM productos
WHERE nombre REGEXP '\\(.*\\)';
nombre
Monitor LG 27" (4K)
Auriculares Sony (inalámbricos)
Funda iPad (10a gen)

El patrón \\(.*\\) busca un paréntesis de apertura literal, seguido de cualquier contenido, seguido de un paréntesis de cierre literal.

Caso práctico: validar formatos de datos

Las expresiones regulares son especialmente útiles para validar que los datos siguen un formato esperado. Imaginemos una tabla clientes donde queremos detectar registros con datos malformados:

-- Encontrar emails con formato potencialmente inválido
SELECT nombre, email
FROM clientes
WHERE email NOT REGEXP '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$';
nombreemail
Pedro Ruizpedro@ruiz@mail.com
Test Userno-es-un-email
Ana Lópezana lopez@correo.com

La consulta encuentra emails que no cumplen el patrón básico: caracteres válidos antes de la arroba, un dominio con al menos un punto y una extensión de dos o más letras.

-- Encontrar teléfonos que no tienen formato español válido
SELECT nombre, telefono
FROM clientes
WHERE telefono NOT REGEXP '^(\\+34)?[6-9][0-9]{8}$';
nombretelefono
Luis Fernández12345
Marta Díaz+34 612 345 678
Roberto Gil555-1234

Este patrón valida que el teléfono comience opcionalmente por +34, seguido de un dígito entre 6 y 9, y exactamente ocho dígitos más. Los teléfonos con espacios, guiones o longitudes incorrectas se detectan como inválidos.

Caso práctico: clasificar datos con patrones

Puedes usar expresiones regulares junto con CASE para clasificar datos dinámicamente:

SELECT
    nombre,
    email,
    CASE
        WHEN email REGEXP '@gmail\\.com$' THEN 'Gmail'
        WHEN email REGEXP '@(outlook|hotmail|live)\\.com$' THEN 'Microsoft'
        WHEN email REGEXP '@yahoo\\.(com|es)$' THEN 'Yahoo'
        WHEN email REGEXP '@[a-z]+\\.edu$' THEN 'Educativo'
        ELSE 'Otro'
    END AS proveedor_email
FROM clientes;
nombreemailproveedor_email
María Garcíamaria@gmail.comGmail
Carlos Lópezcarlos@outlook.comMicrosoft
Ana Ruizana@yahoo.esYahoo
Pedro Sanzpedro@ucm.eduEducativo
Laura Vegalaura@empresa.comOtro

Esta consulta clasifica a los clientes según su proveedor de email usando patrones regulares, lo cual sería imposible de expresar de forma limpia con múltiples condiciones LIKE.

Consideraciones de rendimiento

Las expresiones regulares son más lentas que las comparaciones directas o que LIKE. MySQL no puede usar índices para acelerar búsquedas con REGEXP, por lo que el motor debe examinar cada fila individualmente. Esto significa que en tablas con millones de filas, una consulta con REGEXP puede ser considerablemente más lenta.

Para mitigar este problema, combina las expresiones regulares con otras condiciones que sí puedan usar índices. Por ejemplo, si sabes que solo necesitas buscar entre productos activos, filtra primero por estado:

-- Mejor: el índice en 'activo' reduce las filas antes de aplicar REGEXP
SELECT nombre, referencia
FROM productos
WHERE activo = 1
  AND referencia REGEXP '^ELE-[0-9]{4}$';

La condición activo = 1 se evalúa primero usando un índice (si existe), y la expresión regular solo se aplica a las filas que pasan ese primer filtro.

Resumen de elementos

La siguiente tabla resume los elementos básicos que acabamos de ver:

ElementoSignificadoEjemplo
.Cualquier caráctera.c coincide con abc, a1c
[abc]Uno de los caracteres listados[aeiou] coincide con vocales
[a-z]Rango de caracteres[0-9] coincide con dígitos
[^abc]Cualquier carácter excepto los listados[^0-9] no dígito
\dDígito\\d{3} tres dígitos
\wCarácter de palabra\\w+ una o más letras/dígitos
\sEspacio en blanco\\s+ uno o más espacios
*Cero o más vecesab*c coincide con ac, abc, abbc
+Una o más vecesab+c coincide con abc, abbc
?Cero o una vezcolou?r coincide con color, colour
{n}Exactamente n vecesa{3} coincide con aaa
{n,m}Entre n y m vecesa{2,4} de 2 a 4 aes
^Inicio de cadena^Hola empieza por Hola
$Fin de cadenafin$ termina en fin
|Alternancia (o)gato|perro uno u otro
()Grupo(ab)+ una o más veces ab
\\Escape\\. punto literal

Con estos fundamentos cubiertos, estás preparado para usar expresiones regulares en consultas reales. En el siguiente artículo veremos REGEXP para usar expresiones regulares en condiciones WHERE.

Escrito por Eduardo Lázaro