ITERATE

ITERATE salta el resto del cuerpo del bucle y comienza la siguiente iteración. Es el equivalente a continue en lenguajes como JavaScript, Java o C. Solo se puede usar dentro de LOOP, WHILE y REPEAT.

Mientras que LEAVE termina el bucle completamente, ITERATE lo que hace es decir "esta iteración ya no me interesa, pasa a la siguiente". Es útil cuando necesitas filtrar elementos dentro de un bucle: en lugar de envolver todo el código en un IF, usas ITERATE para saltar los elementos que no cumplen tus criterios y mantienes el código principal sin anidamiento adicional.

Sintaxis

ITERATE etiqueta;

Al igual que LEAVE, necesita una etiqueta que identifique el bucle al que se aplica. No se puede usar fuera de un bucle ni con bloques BEGIN...END (a diferencia de LEAVE, que sí puede salir de bloques).

Ejemplo básico: saltar números pares

El caso más simple de ITERATE es filtrar elementos en un bucle. En lugar de procesar todos los números y usar un IF para decidir si imprimirlos, ITERATE salta directamente los que no nos interesan:

DELIMITER //
 
CREATE PROCEDURE solo_impares(IN limite INT)
BEGIN
    DECLARE i INT DEFAULT 0;
    DECLARE resultado VARCHAR(500) DEFAULT '';
 
    numeros: LOOP
        SET i = i + 1;
 
        IF i > limite THEN
            LEAVE numeros;
        END IF;
 
        -- Saltar números pares
        IF i MOD 2 = 0 THEN
            ITERATE numeros;
        END IF;
 
        SET resultado = CONCAT(resultado, i, ' ');
    END LOOP numeros;
 
    SELECT TRIM(resultado) AS impares;
END //
 
DELIMITER ;
CALL solo_impares(10);
impares
1 3 5 7 9

Cuando i es par, ITERATE salta directamente al inicio del bucle (incrementando primero i en la siguiente iteración). El CONCAT después del ITERATE nunca se ejecuta para los números pares. Esto produce un código más limpio que la alternativa con IF: en lugar de IF i MOD 2 != 0 THEN SET resultado = CONCAT(...); END IF, el ITERATE invierte la lógica y elimina un nivel de anidamiento.

Es importante notar que el incremento de i está antes del ITERATE. Si estuviera después, ITERATE lo saltaría y crearíamos un bucle infinito (i nunca cambiaría). Este es el error más común al usar ITERATE: asegúrate de que la lógica que avanza el bucle se ejecute antes de cualquier ITERATE.

ITERATE en WHILE

ITERATE funciona en cualquier tipo de bucle. En un WHILE, salta al inicio del bucle donde se reevalúa la condición. Esto es útil para filtrar filas de un cursor sin necesidad de anidar toda la lógica de procesamiento dentro de un IF:

DELIMITER //
 
CREATE PROCEDURE productos_caros_con_stock()
BEGIN
    DECLARE v_nombre VARCHAR(100);
    DECLARE v_precio DECIMAL(10,2);
    DECLARE v_stock INT;
    DECLARE v_done INT DEFAULT FALSE;
    DECLARE v_total INT DEFAULT 0;
 
    DECLARE cur CURSOR FOR
        SELECT nombre, precio, stock FROM productos WHERE activo = TRUE ORDER BY precio DESC;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
 
    DROP TEMPORARY TABLE IF EXISTS tmp_resultado;
    CREATE TEMPORARY TABLE tmp_resultado (
        nombre VARCHAR(100),
        precio DECIMAL(10,2),
        stock INT
    );
 
    OPEN cur;
 
    filtrar: WHILE NOT v_done DO
        FETCH cur INTO v_nombre, v_precio, v_stock;
 
        IF v_done THEN
            LEAVE filtrar;
        END IF;
 
        -- Saltar productos baratos
        IF v_precio < 500 THEN
            ITERATE filtrar;
        END IF;
 
        -- Saltar productos sin stock
        IF v_stock = 0 THEN
            ITERATE filtrar;
        END IF;
 
        INSERT INTO tmp_resultado VALUES (v_nombre, v_precio, v_stock);
        SET v_total = v_total + 1;
    END WHILE filtrar;
 
    CLOSE cur;
 
    SELECT * FROM tmp_resultado;
    SELECT v_total AS total_encontrados;
 
    DROP TEMPORARY TABLE tmp_resultado;
END //
 
DELIMITER ;
CALL productos_caros_con_stock();
nombrepreciostock
ASUS ROG Zephyrus1899.9912
Lenovo ThinkPad X11549.0018
MacBook Air M31399.0025
iPhone 15 Pro1299.9945
Samsung Galaxy S24899.9962
Samsung TV QLED 55"749.0020
Google Pixel 8699.0038
Xiaomi 14599.9980
Sofá 3 plazas599.008

Cada ITERATE actúa como un "guardia" que filtra elementos no deseados. El código de procesamiento (INSERT, incremento del contador) solo se ejecuta para las filas que pasan todos los filtros. Sin ITERATE, necesitarías IF v_precio >= 500 AND v_stock > 0 THEN ... END IF, lo cual funciona igual pero con más anidamiento. Con muchos filtros, la diferencia de legibilidad se hace notable.

FizzBuzz con ITERATE

FizzBuzz es un ejercicio clásico de programación que se adapta muy bien para demostrar ITERATE. El truco es verificar las condiciones de mayor a menor prioridad y usar ITERATE para saltar al siguiente número una vez que se ha clasificado el actual:

DELIMITER //
 
CREATE PROCEDURE fizzbuzz(IN limite INT)
BEGIN
    DECLARE i INT DEFAULT 0;
 
    DROP TEMPORARY TABLE IF EXISTS tmp_fizzbuzz;
    CREATE TEMPORARY TABLE tmp_fizzbuzz (
        numero INT,
        valor VARCHAR(10)
    );
 
    fb: LOOP
        SET i = i + 1;
 
        IF i > limite THEN
            LEAVE fb;
        END IF;
 
        IF i MOD 15 = 0 THEN
            INSERT INTO tmp_fizzbuzz VALUES (i, 'FizzBuzz');
            ITERATE fb;
        END IF;
 
        IF i MOD 3 = 0 THEN
            INSERT INTO tmp_fizzbuzz VALUES (i, 'Fizz');
            ITERATE fb;
        END IF;
 
        IF i MOD 5 = 0 THEN
            INSERT INTO tmp_fizzbuzz VALUES (i, 'Buzz');
            ITERATE fb;
        END IF;
 
        INSERT INTO tmp_fizzbuzz VALUES (i, CAST(i AS CHAR));
    END LOOP fb;
 
    SELECT * FROM tmp_fizzbuzz;
    DROP TEMPORARY TABLE tmp_fizzbuzz;
END //
 
DELIMITER ;
CALL fizzbuzz(15);
numerovalor
11
22
3Fizz
44
5Buzz
6Fizz
77
88
9Fizz
10Buzz
1111
12Fizz
1313
1414
15FizzBuzz

Cada ITERATE actúa como un "ya clasificado, pasa al siguiente". El orden importa: i MOD 15 (FizzBuzz) debe ir antes que i MOD 3 (Fizz) y i MOD 5 (Buzz), porque 15 es múltiplo de ambos. Si no se cumple ninguna condición, se ejecuta el INSERT final con el número tal cual.

LEAVE vs ITERATE

Ambas sentencias controlan el flujo dentro de un bucle, pero con efectos opuestos:

SentenciaEfectoEquivalenteCuándo usar
LEAVESale del bucle completamentebreakTerminación anticipada, dato encontrado
ITERATESalta a la siguiente iteracióncontinueFiltrar elementos, saltar casos inválidos

Un patrón habitual es usar ambas en el mismo bucle: LEAVE para la terminación y ITERATE para el filtrado.

Cuándo usar ITERATE vs IF

ITERATE y un IF envolvente producen el mismo resultado, pero la legibilidad difiere. Compara estas dos formas equivalentes:

Con ITERATE (filtro negativo, sin anidamiento):

IF v_precio < 500 THEN ITERATE bucle; END IF;
IF v_stock = 0 THEN ITERATE bucle; END IF;
-- código principal aquí, sin anidamiento

Con IF (filtro positivo, con anidamiento):

IF v_precio >= 500 AND v_stock > 0 THEN
    -- código principal aquí, anidado un nivel
END IF;

Cuando hay 2 o 3 condiciones, la diferencia es menor. Pero cuando hay 5 o más filtros, ITERATE mantiene el código plano mientras que IF produce un anidamiento profundo.

Limpieza

DROP PROCEDURE IF EXISTS solo_impares;
DROP PROCEDURE IF EXISTS productos_caros_con_stock;
DROP PROCEDURE IF EXISTS fizzbuzz;

En el siguiente artículo veremos los cursores, que permiten recorrer un conjunto de resultados fila por fila dentro de un procedimiento.

Escrito por Eduardo Lázaro