SUBSTRING_INDEX

La función SUBSTRING_INDEX extrae una porción de una cadena tomando como referencia las apariciones de un delimitador. En lugar de trabajar con posiciones numéricas como SUBSTRING, razona en términos de "todo lo que hay antes del segundo punto" o "lo que queda después de la última barra", lo que la hace ideal para dividir textos estructurados.

Es la herramienta perfecta para separar emails por la arroba, dominios por los puntos, nombres y apellidos por el espacio, o campos de una línea CSV por la coma. Su gran ventaja sobre la combinación de SUBSTRING con INSTR es que no necesitas calcular posiciones a mano ni preocuparte de sumar o restar uno: indicas el delimitador y cuántas apariciones quieres, y la función hace el resto. En este artículo veremos cómo funciona el conteo positivo y negativo, cómo anidar la función para extraer elementos intermedios y su comportamiento cuando el delimitador no existe.

Sintaxis

SUBSTRING_INDEX recibe la cadena, el delimitador y un número que indica cuántas apariciones del delimitador considerar y en qué dirección.

SUBSTRING_INDEX(cadena, delimitador, conteo)

El signo del conteo determina desde qué lado se cuenta y qué parte se devuelve:

  • conteo positivo: devuelve todo lo que hay antes de la enésima aparición del delimitador, contando desde el principio
  • conteo negativo: devuelve todo lo que hay después de la enésima aparición, contando desde el final

Ejemplo básico

Veámoslo con una dirección web separada por puntos. Con conteo 1, obtenemos todo lo anterior al primer punto:

SELECT SUBSTRING_INDEX('www.mysql.com', '.', 1) AS resultado;
resultado
www

Con conteo 2, la función incluye hasta antes del segundo punto, es decir, los dos primeros segmentos:

SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2) AS resultado;
resultado
www.mysql

El conteo negativo cambia la perspectiva al final de la cadena. Con -1 obtenemos lo que sigue al último delimitador, perfecto para quedarse con la extensión o el último segmento:

-- Conteo negativo: desde el final
SELECT SUBSTRING_INDEX('www.mysql.com', '.', -1) AS resultado;
resultado
com

Y con -2, los dos últimos segmentos:

SELECT SUBSTRING_INDEX('www.mysql.com', '.', -2) AS resultado;
resultado
mysql.com

Separar usuario y dominio de un email

La combinación de conteo positivo y negativo permite partir un email en sus dos mitades de forma muy limpia, sin localizar la arroba manualmente:

SELECT
    email,
    SUBSTRING_INDEX(email, '@', -1) AS dominio,
    SUBSTRING_INDEX(email, '@', 1) AS usuario
FROM clientes
LIMIT 5;
emaildominiousuario
maria.garcia@email.comemail.commaria.garcia
carlos.rodriguez@email.comemail.comcarlos.rodriguez
laura.lopez@email.comemail.comlaura.lopez
pedro.fernandez@email.comemail.compedro.fernandez
ana.martinez@email.comemail.comana.martinez

Comparado con tener que combinar SUBSTRING e INSTR y ajustar posiciones, este enfoque es mucho más directo cuando el delimitador es conocido.

Separar primer y segundo apellido

La misma técnica sirve para dividir un campo de apellidos por el espacio, obteniendo el primero con conteo positivo y el último con conteo negativo:

SELECT
    nombre,
    apellidos,
    SUBSTRING_INDEX(apellidos, ' ', 1) AS primer_apellido,
    SUBSTRING_INDEX(apellidos, ' ', -1) AS segundo_apellido
FROM clientes
LIMIT 5;
nombreapellidosprimer_apellidosegundo_apellido
MaríaGarcía LópezGarcíaLópez
CarlosRodríguez MartínRodríguezMartín
LauraLópez SánchezLópezSánchez
PedroFernández CastroFernándezCastro
AnaMartínez RuizMartínezRuiz

Extraer un campo intermedio anidando la función

Para obtener un elemento que no es ni el primero ni el último, se anida SUBSTRING_INDEX: primero se toman los segmentos hasta el que interesa con conteo positivo, y luego se extrae el último de esos con conteo negativo. Así se puede parsear una línea CSV campo a campo:

SELECT
    SUBSTRING_INDEX('iPhone,1299.99,50', ',', 1) AS nombre,
    SUBSTRING_INDEX(SUBSTRING_INDEX('iPhone,1299.99,50', ',', 2), ',', -1) AS precio,
    SUBSTRING_INDEX('iPhone,1299.99,50', ',', -1) AS stock;
nombrepreciostock
iPhone1299.9950

Esta técnica del anidamiento es el patrón estándar para extraer el campo número N de una cadena delimitada, aunque para muchos campos se vuelve poco legible y conviene valorar otras estrategias.

Extraer la primera palabra

Tomar la primera palabra de un texto es tan sencillo como dividir por el espacio con conteo 1, algo útil para agrupar productos por su marca con GROUP BY:

SELECT
    nombre,
    SUBSTRING_INDEX(nombre, ' ', 1) AS marca
FROM productos
LIMIT 5;
nombremarca
iPhone 15 ProiPhone
MacBook Air M2MacBook
AirPods ProAirPods
iPad AiriPad
Apple Watch Series 9Apple

Cuando el delimitador no existe

Conviene saber qué ocurre si el delimitador no aparece en la cadena. En ese caso, SUBSTRING_INDEX no falla ni devuelve NULL: devuelve la cadena completa, sea cual sea el conteo:

SELECT SUBSTRING_INDEX('sin-punto', '.', 1) AS resultado;
resultado
sin-punto

Este comportamiento es cómodo, porque significa que extraer "la primera palabra" de un texto sin espacios devuelve el texto entero, sin necesidad de casos especiales.

Errores comunes

El malentendido más frecuente es esperar poder extraer un campo intermedio con una sola llamada. Para el segundo, tercero o enésimo elemento (que no sea el último) hay que anidar dos SUBSTRING_INDEX, y olvidarlo lleva a obtener varios campos juntos en lugar de uno solo.

Otro punto a recordar es que el delimitador se compara como texto literal y, además, distingue mayúsculas y minúsculas según la colación. Si el delimitador es más de un carácter, debe coincidir exactamente; y si en tus datos el separador aparece a veces pegado y a veces con espacios, los resultados pueden no ser los que esperas.

Cuándo usar SUBSTRING_INDEX

SUBSTRING_INDEX es la mejor opción para dividir cadenas por un delimitador conocido: emails, dominios, nombres, rutas o líneas CSV sencillas. Cuando el corte depende de una posición numérica y no de un separador, SUBSTRING es lo adecuado; y para sustituir texto en lugar de extraerlo, la herramienta es REPLACE.

En el siguiente artículo veremos REPLACE, que sustituye todas las apariciones de un texto dentro de una cadena.

Escrito por Eduardo Lázaro