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}$';| nombre | referencia |
|---|---|
| Monitor 27 pulgadas | MO275 |
| Teclado mecánico | TC001 |
| Ratón inalámbrico | RA04520 |
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';| nombre | categoria |
|---|---|
| MacBook Air M3 | portátil |
| iPad Pro 12.9 | tablet |
| iPhone 15 Pro | smartphone |
| Samsung Galaxy S24 | smartphone |
| Lenovo ThinkPad X1 | portá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)$';| nombre | |
|---|---|
| María García | maria.garcia@gmail.com |
| Carlos Rodríguez | carlos.rodriguez@outlook.com |
| Laura Sánchez | laura.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,}$';| nombre | |
|---|---|
| Pedro Ruiz | pedro@ruiz@mail.com |
| Test User | no-es-un-email |
| Ana López | ana 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}$';| nombre | telefono |
|---|---|
| Luis Fernández | 12345 |
| Marta Díaz | +34 612 345 678 |
| Roberto Gil | 555-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;| nombre | proveedor_email | |
|---|---|---|
| María García | maria@gmail.com | Gmail |
| Carlos López | carlos@outlook.com | Microsoft |
| Ana Ruiz | ana@yahoo.es | Yahoo |
| Pedro Sanz | pedro@ucm.edu | Educativo |
| Laura Vega | laura@empresa.com | Otro |
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:
| Elemento | Significado | Ejemplo |
|---|---|---|
. | Cualquier carácter | a.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 |
\d | Dígito | \\d{3} tres dígitos |
\w | Carácter de palabra | \\w+ una o más letras/dígitos |
\s | Espacio en blanco | \\s+ uno o más espacios |
* | Cero o más veces | ab*c coincide con ac, abc, abbc |
+ | Una o más veces | ab+c coincide con abc, abbc |
? | Cero o una vez | colou?r coincide con color, colour |
{n} | Exactamente n veces | a{3} coincide con aaa |
{n,m} | Entre n y m veces | a{2,4} de 2 a 4 aes |
^ | Inicio de cadena | ^Hola empieza por Hola |
$ | Fin de cadena | fin$ 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
