DELIMITER

DELIMITER cambia el carácter que MySQL usa para marcar el final de una sentencia. Por defecto es el punto y coma ;, pero al crear procedimientos almacenados necesitas cambiarlo porque el cuerpo del procedimiento contiene puntos y coma internos.

Antes de escribir cualquier procedimiento almacenado, función almacenada o trigger, es imprescindible entender cómo funciona DELIMITER y por qué es necesario. Sin esta pieza, MySQL cortará tu código en el lugar equivocado y obtendrás errores de sintaxis que pueden resultar confusos si no conoces la causa.

El problema

Cuando trabajas con el cliente de línea de comandos de MySQL (o cualquier herramienta que envíe sentencias al servidor), el programa necesita saber dónde termina una sentencia y dónde empieza la siguiente. Por defecto, el punto y coma ; es ese marcador: cada vez que el cliente encuentra un ;, envía todo lo acumulado hasta ese punto al servidor para su ejecución.

Esto funciona perfectamente para sentencias individuales, pero se convierte en un problema cuando intentas crear un procedimiento almacenado. El cuerpo del procedimiento contiene sentencias SQL con sus propios puntos y coma, y el cliente no tiene forma de saber si un ; es parte del procedimiento o el final de la sentencia CREATE:

-- Esto FALLA porque MySQL corta en el primer ;
CREATE PROCEDURE test()
BEGIN
    SELECT 1;   -- MySQL piensa que aquí termina
    SELECT 2;
END;

El cliente envía CREATE PROCEDURE test() BEGIN SELECT 1 al servidor, que es una sentencia incompleta. El resultado es un error de sintaxis que no tiene nada que ver con tu código SQL, sino con cómo el cliente interpreta los delimitadores.

La solución: DELIMITER

La solución es decirle al cliente "temporalmente, deja de usar ; como fin de sentencia y usa otra cosa en su lugar". Eso es exactamente lo que hace DELIMITER:

-- Cambiar el delimitador a //
DELIMITER //
 
CREATE PROCEDURE test()
BEGIN
    SELECT 1;   -- Ahora ; no termina la sentencia
    SELECT 2;
END //
 
-- Restaurar el delimitador original
DELIMITER ;

Al cambiar el delimitador a //, el cliente ya no corta en los ; internos del procedimiento. Solo envía la sentencia completa al servidor cuando encuentra // después del END. Una vez creado el procedimiento, se restaura el delimitador original para poder seguir trabajando con normalidad.

Es un comando del cliente, no de SQL

Un detalle importante que genera confusión: DELIMITER no es una sentencia SQL. No la ejecuta el servidor MySQL, sino el programa cliente (mysql, MySQL Workbench, DBeaver, etc.). Esto tiene varias consecuencias prácticas:

  • No puedes usar DELIMITER dentro de un procedimiento almacenado.
  • No aparece en los dumps de mysqldump (que usa su propia lógica de delimitadores).
  • Algunas herramientas gráficas como phpMyAdmin o MySQL Workbench tienen un campo separado para configurar el delimitador, y en esos casos puede que no necesites escribir DELIMITER explícitamente.
  • Si ejecutas SQL desde un archivo con SOURCE, el delimitador del archivo se aplica correctamente.

Delimitadores comunes

Puedes usar cualquier secuencia de caracteres que no aparezca en el cuerpo del procedimiento. Las opciones más habituales son:

DelimitadorEjemplo
//Más común y legible
$$Muy popular, especialmente en PostgreSQL
;;Doble punto y coma

La convención más extendida en la comunidad MySQL es //. Es visualmente limpio, fácil de teclear y prácticamente imposible que aparezca en el cuerpo de un procedimiento. $$ es la segunda opción más popular y es la que encontrarás en muchos tutoriales y herramientas.

Evita usar delimitadores que puedan confundirse con operadores SQL o caracteres especiales. Por ejemplo, no uses | (operador OR a nivel de bits), # (inicio de comentario en MySQL) o -- (inicio de comentario en SQL estándar).

Ejemplo práctico

DELIMITER //
 
CREATE PROCEDURE productos_por_categoria(IN cat_id INT)
BEGIN
    SELECT nombre, precio
    FROM productos
    WHERE categoria_id = cat_id
    ORDER BY precio DESC;
END //
 
DELIMITER ;
-- Llamar al procedimiento
CALL productos_por_categoria(6);
nombreprecio
iPhone 15 Pro1299.99
Samsung Galaxy S24899.99
Google Pixel 8699.00
Xiaomi 14599.99

Observa el flujo: primero cambias el delimitador, luego defines el procedimiento usando // para terminar la sentencia, y finalmente restauras el ; para que las siguientes sentencias (como el CALL) funcionen con normalidad.

Reglas importantes

Siempre restaura el delimitador. Si olvidas el DELIMITER ; final, todas las sentencias que escribas después seguirán usando // como delimitador, lo que provocará errores inesperados. Es un error frecuente en principiantes y puede ser difícil de diagnosticar si no lo tienes en mente.

No dejes espacio extra después de END. El delimitador debe ir pegado o con un solo espacio después de END. Es decir, END // o END//, pero no END ; // (que MySQL interpretaría como dos sentencias separadas).

El delimitador es por sesión. Cada conexión al servidor tiene su propio delimitador. Si cambias el delimitador en una ventana de tu cliente, no afecta a otras ventanas o conexiones.

En herramientas gráficas, verifica la configuración. MySQL Workbench, por ejemplo, tiene una opción en las preferencias para configurar el delimitador de scripts. phpMyAdmin tiene un campo "Delimiter" debajo del área de texto SQL. En estos casos, puede que no necesites el comando DELIMITER en tu script.

Múltiples procedimientos

Cuando necesitas crear varios procedimientos en un mismo script, no es necesario cambiar el delimitador para cada uno. Puedes envolver todos los CREATE dentro de un solo bloque DELIMITER:

DELIMITER //
 
CREATE PROCEDURE proc_a()
BEGIN
    SELECT 'Procedimiento A';
END //
 
CREATE PROCEDURE proc_b()
BEGIN
    SELECT 'Procedimiento B';
END //
 
DELIMITER ;

Esto es habitual en scripts de migración o de inicialización de base de datos, donde se crean decenas de procedimientos de una vez. Un solo DELIMITER // al inicio y un solo DELIMITER ; al final.

Limpieza

DROP PROCEDURE IF EXISTS test;
DROP PROCEDURE IF EXISTS productos_por_categoria;
DROP PROCEDURE IF EXISTS proc_a;
DROP PROCEDURE IF EXISTS proc_b;

En el siguiente artículo veremos la sintaxis completa de CREATE PROCEDURE con todas sus opciones.

Escrito por Eduardo Lázaro