LEAVE

LEAVE sale de un bucle o de un bloque BEGIN...END etiquetado. Es el equivalente a break en lenguajes como JavaScript, Java o C. Se usa con LOOP, WHILE, REPEAT y bloques BEGIN...END.

Hasta ahora, las condiciones de los bucles WHILE y REPEAT controlan la terminación de forma estructurada. Pero hay situaciones donde necesitas salir de un bucle antes de que la condición normal se cumpla: encontraste el dato que buscabas, detectaste un error, o una validación falló y no tiene sentido seguir iterando. LEAVE te da esa capacidad de salida inmediata.

Además de salir de bucles, LEAVE puede salir de bloques BEGIN...END etiquetados, lo que permite implementar un patrón similar al "return early" de los lenguajes de programación: si una condición no se cumple, sales del bloque sin ejecutar el resto del código.

Sintaxis

LEAVE etiqueta;

La etiqueta debe coincidir exactamente con la que definiste en el bucle o bloque del que quieres salir. Si la etiqueta no existe o está fuera del ámbito actual, MySQL genera un error de compilación.

LEAVE en un LOOP

El uso más habitual de LEAVE es en combinación con LOOP, donde es la única forma de terminar el bucle. En este ejemplo, recorremos productos con un cursor y salimos en cuanto encontramos uno que cumpla la condición:

DELIMITER //
 
CREATE PROCEDURE buscar_primer_caro(IN umbral DECIMAL(10,2))
BEGIN
    DECLARE v_id INT;
    DECLARE v_nombre VARCHAR(100);
    DECLARE v_precio DECIMAL(10,2);
    DECLARE v_done INT DEFAULT FALSE;
 
    DECLARE cur CURSOR FOR
        SELECT id, nombre, precio FROM productos ORDER BY id;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
 
    OPEN cur;
 
    buscar: LOOP
        FETCH cur INTO v_id, v_nombre, v_precio;
 
        IF v_done THEN
            LEAVE buscar;
        END IF;
 
        -- Salir cuando encontramos el primer producto caro
        IF v_precio >= umbral THEN
            SELECT v_id AS id, v_nombre AS nombre, v_precio AS precio,
                   'Primer producto encontrado' AS mensaje;
            LEAVE buscar;
        END IF;
    END LOOP buscar;
 
    CLOSE cur;
END //
 
DELIMITER ;
CALL buscar_primer_caro(1500);
idnombrepreciomensaje
6Lenovo ThinkPad X11549.00Primer producto encontrado

Observa que hay dos sentencias LEAVE en el mismo bucle: una para cuando el cursor se agota (v_done) y otra para cuando encontramos el producto buscado. Ambas salen del mismo bucle buscar, pero por razones diferentes. Este patrón de "múltiples puntos de salida" es perfectamente válido y es una de las ventajas de LEAVE sobre depender solo de la condición del bucle.

LEAVE en un WHILE

Aunque WHILE ya tiene su propia condición de terminación, LEAVE permite salir por una razón adicional que no se puede expresar fácilmente en la condición del WHILE:

DELIMITER //
 
CREATE PROCEDURE sumar_hasta_limite(IN limite DECIMAL(10,2))
BEGIN
    DECLARE v_nombre VARCHAR(100);
    DECLARE v_precio DECIMAL(10,2);
    DECLARE v_suma DECIMAL(10,2) DEFAULT 0;
    DECLARE v_contador INT DEFAULT 0;
    DECLARE v_done INT DEFAULT FALSE;
 
    DECLARE cur CURSOR FOR
        SELECT nombre, precio FROM productos
        WHERE activo = TRUE ORDER BY precio DESC;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
 
    OPEN cur;
 
    acumular: WHILE NOT v_done DO
        FETCH cur INTO v_nombre, v_precio;
 
        IF v_done THEN
            LEAVE acumular;
        END IF;
 
        IF v_suma + v_precio > limite THEN
            LEAVE acumular;
        END IF;
 
        SET v_suma = v_suma + v_precio;
        SET v_contador = v_contador + 1;
    END WHILE acumular;
 
    CLOSE cur;
 
    SELECT v_contador AS productos,
           v_suma AS total,
           limite AS presupuesto,
           limite - v_suma AS restante;
END //
 
DELIMITER ;
CALL sumar_hasta_limite(5000);
productostotalpresupuestorestante
44947.985000.0052.02

Aquí el WHILE controla la condición general (NOT v_done), pero LEAVE se encarga de la salida por exceso de presupuesto. Separar las condiciones de salida así hace el código más legible: cada LEAVE tiene una razón clara y específica.

LEAVE en un bloque BEGIN...END

Esta es una capacidad de LEAVE que no todos los desarrolladores conocen: puede salir de bloques BEGIN...END etiquetados, no solo de bucles. Esto permite implementar salidas tempranas, un patrón donde verificas precondiciones al inicio y sales si alguna falla, evitando anidar todo el código dentro de múltiples IF:

DELIMITER //
 
CREATE PROCEDURE verificar_y_procesar(IN prod_id INT)
BEGIN
    DECLARE v_existe INT;
    DECLARE v_activo BOOLEAN;
    DECLARE v_stock INT;
    DECLARE v_nombre VARCHAR(100);
 
    principal: BEGIN
        -- Verificar que existe
        SELECT COUNT(*) INTO v_existe FROM productos WHERE id = prod_id;
        IF v_existe = 0 THEN
            SELECT 'Producto no encontrado' AS error;
            LEAVE principal;
        END IF;
 
        -- Verificar que está activo
        SELECT activo, stock, nombre INTO v_activo, v_stock, v_nombre
        FROM productos WHERE id = prod_id;
 
        IF v_activo = FALSE THEN
            SELECT 'Producto no activo' AS error;
            LEAVE principal;
        END IF;
 
        -- Verificar stock
        IF v_stock = 0 THEN
            SELECT 'Producto sin stock' AS error;
            LEAVE principal;
        END IF;
 
        -- Si llegamos aquí, todo está bien
        SELECT v_nombre AS producto, v_stock AS stock, 'OK' AS estado;
    END principal;
END //
 
DELIMITER ;
CALL verificar_y_procesar(1);
productostockestado
iPhone 15 Pro45OK
CALL verificar_y_procesar(999);
error
Producto no encontrado

Sin LEAVE, este código necesitaría IF anidados: IF existe THEN IF activo THEN IF stock > 0 THEN ... END IF END IF END IF. Con LEAVE, cada validación es independiente y el código fluye de arriba a abajo sin anidamiento. Este patrón es especialmente valioso en procedimientos con muchas validaciones previas.

LEAVE con bucles anidados

Cuando tienes bucles anidados, LEAVE sale del bucle cuya etiqueta especifiques. Esto permite salir directamente del bucle exterior desde el interior, lo que no es posible con un simple break en muchos lenguajes de programación:

DELIMITER //
 
CREATE PROCEDURE buscar_en_matriz()
BEGIN
    DECLARE i INT DEFAULT 1;
    DECLARE j INT;
    DECLARE encontrado BOOLEAN DEFAULT FALSE;
 
    exterior: LOOP
        IF i > 10 THEN
            LEAVE exterior;
        END IF;
 
        SET j = 1;
 
        interior: LOOP
            IF j > 10 THEN
                LEAVE interior;
            END IF;
 
            IF i * j = 42 THEN
                SELECT i AS fila, j AS columna,
                       CONCAT(i, ' x ', j, ' = 42') AS resultado;
                SET encontrado = TRUE;
                LEAVE exterior;  -- Sale del bucle exterior
            END IF;
 
            SET j = j + 1;
        END LOOP interior;
 
        SET i = i + 1;
    END LOOP exterior;
 
    IF NOT encontrado THEN
        SELECT 'No encontrado' AS resultado;
    END IF;
END //
 
DELIMITER ;
CALL buscar_en_matriz();
filacolumnaresultado
676 x 7 = 42

La clave está en LEAVE exterior: desde el bucle interior, saltamos directamente fuera del bucle exterior, terminando ambas iteraciones de un solo golpe. Sin etiquetas, necesitaríamos una variable flag y comprobaciones adicionales en ambos bucles para lograr el mismo efecto.

LEAVE vs reestructurar la lógica

LEAVE es una herramienta poderosa, pero úsala con criterio. Un procedimiento con muchas sentencias LEAVE dispersas puede ser difícil de seguir. Antes de añadir un LEAVE, pregúntate si la lógica se podría expresar mejor reorganizando las condiciones. Las salidas tempranas de bloques BEGIN...END (como en el ejemplo de validaciones) son un uso excelente. Los LEAVE en bucles con cursores son el patrón estándar. Pero si te encuentras con más de 3 o 4 puntos de salida en un solo bucle, probablemente el procedimiento se beneficiaría de una reestructuración.

Limpieza

DROP PROCEDURE IF EXISTS buscar_primer_caro;
DROP PROCEDURE IF EXISTS sumar_hasta_limite;
DROP PROCEDURE IF EXISTS verificar_y_procesar;
DROP PROCEDURE IF EXISTS buscar_en_matriz;

En el siguiente artículo veremos ITERATE, que salta a la siguiente iteración de un bucle.

Escrito por Eduardo Lázaro