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. El salto de calidad fue significativo: el motor anterior (basado en la implementación de Henry Spencer) no soportaba capturas con nombre, Unicode multibyte ni modos de coincidencia, lo que limitaba gravemente su utilidad en aplicaciones reales con datos internacionales.

Antes de profundizar en cada función de expresiones regulares que ofrece MySQL, este artículo cubre los fundamentos que necesitas dominar: la sintaxis básica de los patrones, las clases de caracteres, los cuantificadores, las anclas, la alternancia y los grupos. Con estos elementos serás capaz de construir patrones para la gran mayoría de tareas de búsqueda y validación que surgen en el trabajo diario con bases de datos.

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. 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. 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]';

La diferencia es clara: LIKE trata [0-9] como texto literal, no como un rango de dígitos. Las expresiones regulares, en cambio, interpretan [0-9] como "cualquier carácter del 0 al 9", [A-Za-z]+ como "una o más letras" y .* como "cualquier secuencia de cualquier longitud". Esta capacidad de describir patrones con precisión es lo que hace a las expresiones regulares indispensables cuando LIKE se queda corto.

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. Esta recomendación no es solo una cuestión de estilo; las expresiones regulares tienen un coste de procesamiento mayor porque el motor debe evaluar cada carácter contra un autómata finito, mientras que LIKE realiza comparaciones mucho más simples.

Sintaxis

MySQL ofrece un operador y cuatro funciones para trabajar con expresiones regulares. El operador REGEXP (y su sinónimo RLIKE) se usa dentro de la cláusula WHERE para filtrar filas cuyo contenido coincida con un patrón. Las cuatro funciones, disponibles desde MySQL 8.0, amplían las posibilidades: permiten encontrar la posición de una coincidencia, reemplazar texto o extraer subcadenas.

-- 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 donde ^ y $ coinciden con inicio y fin de cada línea, no solo de la cadena completa). Puedes combinar varios modos en una misma cadena, por ejemplo 'im' para insensible a mayúsculas y multilínea. Lo veremos en detalle en los artículos de cada función: REGEXP, REGEXP_LIKE, REGEXP_INSTR, REGEXP_REPLACE y REGEXP_SUBSTR.

Clases de caracteres

Las clases de caracteres permiten indicar un conjunto de caracteres que pueden aparecer en una posición determinada del patrón. Se escriben entre corchetes y el motor de expresiones regulares intenta hacer coincidir exactamente uno de los caracteres listados con el carácter en la posición actual de la cadena evaluada.

-- [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. Si la cadena fuera pato, el resultado sería 0 porque p no está en el conjunto [gc].

Los rangos dentro de los corchetes simplifican la escritura de secuencias consecutivas. En lugar de listar cada carácter individualmente, puedes usar un guion para indicar un rango completo basado en el orden del código de caracteres.

-- [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. Los rangos se pueden combinar dentro de los mismos corchetes: [a-zA-Z0-9] coincide con cualquier letra o dígito.

El acento circunflejo dentro de los corchetes niega la clase, es decir, coincide con cualquier carácter que no esté en el conjunto especificado. Este mecanismo es particularmente útil para detectar datos que contienen caracteres inesperados.

-- [^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 en una campaña de limpieza de la base de datos.

MySQL también soporta secuencias de escape predefinidas que actúan como atajos de clases de caracteres comunes. Estas secuencias son especialmente cómodas porque evitan tener que escribir rangos largos.

-- \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. Este es uno de los puntos que más confusión genera entre desarrolladores que vienen de otros lenguajes como JavaScript o Python, donde solo se necesita una barra. Si usas una sola barra, MySQL interpreta la secuencia como un escape de cadena y el motor de regex no recibe el patrón correcto.

Cuantificadores

Los cuantificadores indican cuántas veces debe repetirse el elemento anterior para que haya coincidencia. Sin cuantificadores, cada elemento del patrón coincide exactamente una vez. Los cuantificadores amplían esta capacidad permitiendo coincidencias opcionales, repetidas o con un número específico de ocurrencias.

-- *    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 que muestra el poder de los cuantificadores en un escenario real. 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. Sin las anclas ^ y $, el patrón podría coincidir parcialmente con cadenas más largas que contuvieran esa secuencia en cualquier posición, lo cual no sería el comportamiento deseado.

El cuantificador ? hace que un elemento sea opcional, es decir, que pueda aparecer cero o una vez. Es especialmente útil para patrones que pueden tener variantes ortográficas o formatos ligeramente diferentes.

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

La diferencia entre * y + es sutil pero importante: * permite cero ocurrencias (es decir, la ausencia del elemento es una coincidencia válida), mientras que + exige al menos una. Si usas [0-9]* en un patrón, la cadena vacía es una coincidencia válida para esa parte, lo que a menudo produce resultados inesperados. En general, + es más seguro cuando sabes que el elemento debe estar presente al menos una vez.

Anclas

Las anclas no coinciden con caracteres, sino con posiciones dentro de la cadena. Son fundamentales para evitar coincidencias parciales no deseadas, ya que por defecto REGEXP busca el patrón en cualquier parte de la cadena, no necesariamente al principio o al final.

-- ^  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. Entender la diferencia entre buscar un patrón "en algún lugar de la cadena" y exigir que "toda la cadena coincida con el patrón" es probablemente el concepto más importante de las expresiones regulares.

Un caso práctico habitual es validar que un campo contiene exactamente un formato esperado. Muchas bases de datos acumulan registros con formatos inconsistentes a lo largo del tiempo, y las expresiones regulares con anclas permiten detectar esas anomalías.

-- 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. El patrón ^[0-5][0-9]{4}$ exige que el primer dígito esté entre 0 y 5 (las provincias españolas van del 01 al 52) y que los cuatro dígitos restantes sean cualquier número. Cualquier código postal que no cumpla estrictamente este formato aparecerá en los resultados.

Alternancia

El operador | funciona como un "o" lógico entre patrones. Permite especificar varias alternativas y el motor intentará hacer coincidir cada una de izquierda a derecha hasta encontrar una que tenga éxito. La alternancia es uno de los mecanismos más utilizados en expresiones regulares porque permite construir patrones que aceptan múltiples variantes de un mismo concepto.

-- 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 para crear patrones más específicos.

-- 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 o gmail5com, algo que probablemente no quieres. Este es un error muy común que puede pasar desapercibido durante meses si no se verifica con datos de prueba variados.

Grupos

Los paréntesis () tienen dos funciones en las expresiones regulares: agrupan elementos para aplicarles cuantificadores o alternancia, y crean referencias que se pueden usar en reemplazos con REGEXP_REPLACE. Entender cuándo y por qué usar paréntesis marca la diferencia entre patrones que funcionan correctamente y patrones que producen coincidencias inesperadas.

-- 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 paréntesis convierten la en una unidad que se repite como bloque, produciendo lala, lalala, etc.

Los grupos también permiten aplicar alternancia a una parte del patrón sin que la alternancia afecte al resto de la expresión. Este uso es imprescindible cuando necesitas combinar alternativas con texto fijo.

-- 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. Sin los paréntesis, el patrón ^Calle|Avenida|Plaza|Paseo significaría "empieza por Calle, o contiene Avenida en cualquier parte, o contiene Plaza en cualquier parte, o contiene Paseo seguido de espacio", que es una lógica completamente diferente.

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, lo cual es una fuente constante de errores para desarrolladores que no están familiarizados con este mecanismo de doble escape.

-- 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. Si olvidaras las barras y escribieras (.*), los paréntesis actuarían como grupo de captura en lugar de buscar paréntesis literales, y el patrón coincidiría con cualquier cadena de cualquier longitud, dando resultados completamente diferentes.

Caso práctico: validar formatos de datos

Las expresiones regulares son especialmente útiles para validar que los datos siguen un formato esperado. En bases de datos que llevan años en producción, es habitual encontrar registros con formatos inconsistentes porque las validaciones del frontend no siempre fueron estrictas o porque los datos se importaron desde fuentes externas. 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. Este patrón no es una validación perfecta de emails (la especificación RFC 5322 es extremadamente compleja), pero detecta los errores más comunes como doble arroba, espacios o ausencia de dominio.

La validación de teléfonos es otro caso frecuente donde las expresiones regulares resultan imprescindibles, ya que los números de teléfono se introducen en formatos muy variados.

-- 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. Una vez detectados, podrías usar REGEXP_REPLACE para limpiar automáticamente los formatos eliminando espacios y guiones.

Caso práctico: clasificar datos con patrones

Puedes usar expresiones regulares junto con CASE para clasificar datos dinámicamente. Esta técnica permite crear columnas calculadas que categorizan registros en función de patrones de texto, algo que sería extremadamente verboso o directamente imposible de lograr con condiciones LIKE.

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. La alternancia (outlook|hotmail|live) agrupa tres dominios de Microsoft en una sola condición, algo que con LIKE requeriría tres condiciones separadas unidas con OR. A medida que añades más proveedores o patrones más complejos, la ventaja de las expresiones regulares sobre LIKE crece exponencialmente.

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 que una equivalente con LIKE o con comparaciones de igualdad.

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 usando WHERE.

-- 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. Este principio se aplica a cualquier combinación de condiciones: coloca siempre las condiciones más restrictivas y que puedan usar índices antes de las expresiones regulares. El optimizador de MySQL generalmente hace esto automáticamente, pero es buena práctica escribir las condiciones en el orden lógico correcto.

Otra consideración es que los patrones más complejos (con muchas alternativas, cuantificadores anidados o lookahead/lookbehind) son más costosos de evaluar que los patrones simples. Si un patrón tarda demasiado, intenta simplificarlo o divídelo en varias condiciones más simples.

Errores comunes al empezar

El error más frecuente entre principiantes es olvidar que REGEXP busca el patrón en cualquier parte de la cadena, no solo al principio. Si escribes WHERE nombre REGEXP '[0-9]', encontrarás todas las filas que contengan al menos un dígito en cualquier posición, no las que empiecen por un dígito. Para exigir que el dígito esté al principio, necesitas el ancla ^: WHERE nombre REGEXP '^[0-9]'.

Otro error habitual es confundir el escape de cadena SQL con el escape de regex. En MySQL, para representar el patrón \d (un dígito) en una cadena SQL, necesitas escribir '\\d'. Si solo escribes '\d', MySQL interpreta \d como un escape de cadena (que no tiene significado especial) y puede que el patrón no funcione como esperas. Este problema de doble escape es la principal fuente de frustración al trabajar con regex en SQL.

Finalmente, ten cuidado con la sensibilidad a mayúsculas. Por defecto, REGEXP en MySQL es insensible a mayúsculas para collations que terminan en _ci (case insensitive), que son las más comunes. Si necesitas una comparación sensible a mayúsculas, usa el modo 'c' con REGEXP_LIKE o la palabra clave BINARY antes de la cadena.

Resumen de elementos

La siguiente tabla resume los elementos básicos que acabamos de ver. Tenerla como referencia te será útil mientras construyes tus primeros patrones, hasta que la sintaxis se convierta en algo natural.

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, con ejemplos completos de filtrado, negación y combinación con otras cláusulas.

Escrito por Eduardo Lázaro