WHILE

El bucle WHILE ejecuta un bloque de sentencias repetidamente mientras una condición sea verdadera. La condición se evalúa antes de cada iteración, por lo que si es falsa desde el inicio, el cuerpo nunca se ejecuta.

Sintaxis

[etiqueta:] WHILE condicion DO
    sentencias;
END WHILE [etiqueta];

Ejemplo básico

DELIMITER //
 
CREATE PROCEDURE cuenta_regresiva(IN inicio INT)
BEGIN
    DECLARE resultado VARCHAR(500) DEFAULT '';
 
    WHILE inicio > 0 DO
        SET resultado = CONCAT(resultado, inicio, ' ');
        SET inicio = inicio - 1;
    END WHILE;
 
    SELECT TRIM(resultado) AS cuenta;
END //
 
DELIMITER ;
CALL cuenta_regresiva(5);
cuenta
5 4 3 2 1

Calcular factorial

DELIMITER //
 
CREATE PROCEDURE factorial(IN n INT, OUT resultado BIGINT)
BEGIN
    SET resultado = 1;
 
    WHILE n > 1 DO
        SET resultado = resultado * n;
        SET n = n - 1;
    END WHILE;
END //
 
DELIMITER ;
CALL factorial(5, @fact);
SELECT @fact AS factorial_5;
factorial_5
120
CALL factorial(10, @fact);
SELECT @fact AS factorial_10;
factorial_10
3628800

WHILE con datos

DELIMITER //
 
CREATE PROCEDURE top_productos_por_valor(IN limite INT)
BEGIN
    DECLARE v_contador INT DEFAULT 0;
 
    DROP TEMPORARY TABLE IF EXISTS tmp_ranking;
    CREATE TEMPORARY TABLE tmp_ranking (
        posicion INT,
        nombre VARCHAR(100),
        valor_stock DECIMAL(12,2)
    );
 
    INSERT INTO tmp_ranking
    SELECT NULL, nombre, precio * stock AS valor
    FROM productos
    WHERE activo = TRUE
    ORDER BY valor DESC;
 
    -- Asignar posiciones manualmente
    SET @pos = 0;
    UPDATE tmp_ranking SET posicion = (@pos := @pos + 1);
 
    SELECT posicion, nombre, valor_stock
    FROM tmp_ranking
    WHERE posicion <= limite;
 
    DROP TEMPORARY TABLE tmp_ranking;
END //
 
DELIMITER ;
CALL top_productos_por_valor(5);

WHILE con acumulador

DELIMITER //
 
CREATE PROCEDURE sumar_precios_hasta(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;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE;
 
    OPEN cur;
 
    FETCH cur INTO v_nombre, v_precio;
 
    WHILE NOT v_done AND (v_suma + v_precio) <= limite DO
        SET v_suma = v_suma + v_precio;
        SET v_contador = v_contador + 1;
        FETCH cur INTO v_nombre, v_precio;
    END WHILE;
 
    CLOSE cur;
 
    SELECT v_contador AS productos_incluidos,
           v_suma AS total_acumulado,
           limite AS presupuesto;
END //
 
DELIMITER ;
CALL sumar_precios_hasta(500.00);
productos_incluidostotal_acumuladopresupuesto
7447.89500.00

Condición falsa desde el inicio

DELIMITER //
 
CREATE PROCEDURE ejemplo_while_vacio()
BEGIN
    DECLARE i INT DEFAULT 10;
    DECLARE ejecuto VARCHAR(5) DEFAULT 'No';
 
    -- La condición es falsa desde el inicio
    WHILE i < 5 DO
        SET ejecuto = 'Sí';
        SET i = i + 1;
    END WHILE;
 
    SELECT ejecuto AS se_ejecuto, i AS valor_i;
END //
 
DELIMITER ;
CALL ejemplo_while_vacio();
se_ejecutovalor_i
No10

El cuerpo del WHILE no se ejecuta ni una vez porque 10 < 5 es falso.

Limpieza

DROP PROCEDURE IF EXISTS cuenta_regresiva;
DROP PROCEDURE IF EXISTS factorial;
DROP PROCEDURE IF EXISTS top_productos_por_valor;
DROP PROCEDURE IF EXISTS sumar_precios_hasta;
DROP PROCEDURE IF EXISTS ejemplo_while_vacio;

En el siguiente artículo veremos el bucle REPEAT, que garantiza al menos una ejecución del cuerpo.

Escrito por Eduardo Lázaro