Sentencia IF
La sentencia IF permite ejecutar diferentes bloques de código según una condición. Es la estructura de control de flujo más básica dentro de un procedimiento almacenado.
Hasta ahora, los procedimientos que hemos visto ejecutan sus sentencias en orden secuencial: primero esta consulta, luego esta otra, después el SELECT final. Pero la verdadera potencia de los procedimientos aparece cuando puedes tomar decisiones: si el stock es suficiente, procesa el pedido; si no, devuelve un mensaje de error. Si el cliente es VIP, aplica un descuento mayor. La sentencia IF es la herramienta que permite este tipo de lógica condicional.
Sintaxis
MySQL ofrece tres formas de la sentencia IF, desde la más simple hasta la más completa:
-- IF simple
IF condicion THEN
sentencias;
END IF;
-- IF...ELSE
IF condicion THEN
sentencias;
ELSE
sentencias;
END IF;
-- IF...ELSEIF...ELSE
IF condicion1 THEN
sentencias;
ELSEIF condicion2 THEN
sentencias;
ELSEIF condicion3 THEN
sentencias;
ELSE
sentencias;
END IF;Es importante no confundir la sentencia IF del procedimiento (que ocupa varias líneas y termina con END IF) con la función IF() de MySQL (que se usa en SELECTs como IF(condicion, valor_si, valor_no)). Son mecanismos diferentes: la sentencia IF controla el flujo de ejecución del procedimiento, mientras que la función IF() es una expresión que devuelve un valor.
IF simple
La forma más básica ejecuta un bloque de código solo si la condición es verdadera. Si la condición es falsa, no hace nada y continúa con la siguiente sentencia después del END IF:
DELIMITER //
CREATE PROCEDURE verificar_stock(IN prod_id INT)
BEGIN
DECLARE v_stock INT;
DECLARE v_nombre VARCHAR(100);
SELECT nombre, stock INTO v_nombre, v_stock
FROM productos WHERE id = prod_id;
IF v_stock = 0 THEN
SELECT v_nombre AS producto, 'SIN STOCK' AS estado;
END IF;
IF v_stock > 0 THEN
SELECT v_nombre AS producto, 'Disponible' AS estado, v_stock AS unidades;
END IF;
END //
DELIMITER ;CALL verificar_stock(1);| producto | estado | unidades |
|---|---|---|
| iPhone 15 Pro | Disponible | 45 |
Aunque este ejemplo funciona, usar dos IF separados no es la mejor opción: si el stock fuera NULL (producto no encontrado), ninguno de los dos IF se ejecutaría y el procedimiento no devolvería nada. Además, ambas condiciones se evalúan siempre, aunque sean mutuamente excluyentes. Para estos casos es mejor usar IF...ELSE.
IF...ELSE
Cuando necesitas exactamente dos caminos (esto o lo otro), IF...ELSE garantiza que siempre se ejecute uno de los dos bloques:
DELIMITER //
CREATE PROCEDURE clasificar_precio(IN prod_id INT)
BEGIN
DECLARE v_nombre VARCHAR(100);
DECLARE v_precio DECIMAL(10,2);
SELECT nombre, precio INTO v_nombre, v_precio
FROM productos WHERE id = prod_id;
IF v_precio > 500 THEN
SELECT v_nombre AS producto, v_precio AS precio, 'Premium' AS categoria;
ELSE
SELECT v_nombre AS producto, v_precio AS precio, 'Estándar' AS categoria;
END IF;
END //
DELIMITER ;CALL clasificar_precio(1);| producto | precio | categoria |
|---|---|---|
| iPhone 15 Pro | 1299.99 | Premium |
CALL clasificar_precio(22);| producto | precio | categoria |
|---|---|---|
| Camiseta algodón básica | 19.99 | Estándar |
Con IF...ELSE, siempre hay un resultado. Si el precio es mayor que 500, se devuelve "Premium"; en cualquier otro caso (incluido NULL), se devuelve "Estándar". Esto hace que el procedimiento sea más predecible y fácil de razonar.
IF...ELSEIF...ELSE
Cuando necesitas clasificar un valor en más de dos categorías, puedes encadenar múltiples condiciones con ELSEIF. MySQL evalúa las condiciones de arriba a abajo y ejecuta el bloque de la primera que sea verdadera. Si ninguna es verdadera, ejecuta el bloque ELSE:
DELIMITER //
CREATE PROCEDURE nivel_stock(IN prod_id INT)
BEGIN
DECLARE v_nombre VARCHAR(100);
DECLARE v_stock INT;
DECLARE v_nivel VARCHAR(20);
SELECT nombre, stock INTO v_nombre, v_stock
FROM productos WHERE id = prod_id;
IF v_stock = 0 THEN
SET v_nivel = 'Agotado';
ELSEIF v_stock < 20 THEN
SET v_nivel = 'Bajo';
ELSEIF v_stock < 50 THEN
SET v_nivel = 'Normal';
ELSEIF v_stock < 100 THEN
SET v_nivel = 'Alto';
ELSE
SET v_nivel = 'Sobrestock';
END IF;
SELECT v_nombre AS producto, v_stock AS stock, v_nivel AS nivel;
END //
DELIMITER ;CALL nivel_stock(7);| producto | stock | nivel |
|---|---|---|
| ASUS ROG Zephyrus | 12 | Bajo |
CALL nivel_stock(4);| producto | stock | nivel |
|---|---|---|
| Xiaomi 14 | 80 | Alto |
El orden de las condiciones importa. Como MySQL ejecuta la primera que sea verdadera, las condiciones deben ir de la más restrictiva a la menos restrictiva. Si pusieras v_stock < 100 antes que v_stock < 20, todos los productos con stock menor a 100 entrarían en esa rama y nunca llegarían a "Bajo". Es el mismo principio que en cualquier cadena de if/else if en otros lenguajes.
IF anidados
Puedes anidar sentencias IF dentro de otras sentencias IF para modelar lógica más compleja. Cada IF interior necesita su propio END IF:
DELIMITER //
CREATE PROCEDURE evaluar_producto(IN prod_id INT)
BEGIN
DECLARE v_nombre VARCHAR(100);
DECLARE v_precio DECIMAL(10,2);
DECLARE v_stock INT;
DECLARE v_mensaje VARCHAR(200);
SELECT nombre, precio, stock INTO v_nombre, v_precio, v_stock
FROM productos WHERE id = prod_id;
IF v_stock > 0 THEN
IF v_precio > 1000 THEN
SET v_mensaje = 'Producto premium disponible';
ELSE
SET v_mensaje = 'Producto disponible';
END IF;
ELSE
IF v_precio > 1000 THEN
SET v_mensaje = 'Producto premium AGOTADO - Reponer urgente';
ELSE
SET v_mensaje = 'Producto agotado';
END IF;
END IF;
SELECT v_nombre AS producto, v_precio AS precio, v_stock AS stock, v_mensaje AS mensaje;
END //
DELIMITER ;Los IF anidados son poderosos pero pueden volverse difíciles de leer si se anidan demasiados niveles (tres o más niveles de anidamiento suelen ser señal de que el código necesita reestructurarse). Cuando la lógica se vuelve compleja, considera extraer parte de ella a otro procedimiento o usar CASE para las clasificaciones de valores.
Ejemplo práctico: validar pedido
Este ejemplo muestra un patrón habitual en producción: una serie de validaciones secuenciales donde cada una puede abortar la operación. Las condiciones se evalúan en orden, y la primera que falle determina el mensaje de error:
DELIMITER //
CREATE PROCEDURE validar_pedido(
IN p_cliente_id INT,
IN p_producto_id INT,
IN p_cantidad INT,
OUT p_valido BOOLEAN,
OUT p_mensaje VARCHAR(200)
)
BEGIN
DECLARE v_cliente_existe INT;
DECLARE v_stock INT;
DECLARE v_activo BOOLEAN;
SET p_valido = FALSE;
-- Verificar cliente
SELECT COUNT(*) INTO v_cliente_existe
FROM clientes WHERE id = p_cliente_id;
IF v_cliente_existe = 0 THEN
SET p_mensaje = 'Cliente no encontrado';
ELSE
-- Verificar producto
SELECT stock, activo INTO v_stock, v_activo
FROM productos WHERE id = p_producto_id;
IF v_activo = FALSE THEN
SET p_mensaje = 'Producto no activo';
ELSEIF v_stock = 0 THEN
SET p_mensaje = 'Producto sin stock';
ELSEIF v_stock < p_cantidad THEN
SET p_mensaje = CONCAT('Stock insuficiente. Disponible: ', v_stock);
ELSE
SET p_valido = TRUE;
SET p_mensaje = 'Pedido válido';
END IF;
END IF;
END //
DELIMITER ;CALL validar_pedido(1, 1, 2, @valido, @msg);
SELECT @valido AS valido, @msg AS mensaje;| valido | mensaje |
|---|---|
| 1 | Pedido válido |
Este patrón de "validar primero, ejecutar después" es uno de los usos más comunes de IF en procedimientos almacenados. Cada validación que falla establece el mensaje de error apropiado y evita que se ejecuten las validaciones siguientes (gracias a la estructura ELSE). Esto produce mensajes de error claros y específicos para cada caso de fallo.
Buenas prácticas
Siempre incluye un ELSE final. Aunque no sea obligatorio, tener un ELSE garantiza que tu procedimiento produzca un resultado en todos los casos. Un procedimiento que a veces no devuelve nada es difícil de depurar y propenso a errores en el código que lo llama.
Evalúa primero los casos más comunes. Si sabes que el 90% de los productos tendrán stock disponible, pon esa condición primero. Aunque la diferencia de rendimiento es insignificante, la legibilidad mejora cuando el caso más probable está arriba.
Cuidado con NULL en las condiciones. Si una variable puede ser NULL (por ejemplo, porque SELECT INTO no encontró filas), cualquier comparación con NULL devuelve NULL (que se trata como FALSE). Usa IS NULL para verificar explícitamente: IF v_precio IS NULL THEN ... END IF.
Limpieza
DROP PROCEDURE IF EXISTS verificar_stock;
DROP PROCEDURE IF EXISTS clasificar_precio;
DROP PROCEDURE IF EXISTS nivel_stock;
DROP PROCEDURE IF EXISTS evaluar_producto;
DROP PROCEDURE IF EXISTS validar_pedido;En el siguiente artículo veremos la sentencia CASE, una alternativa más limpia al IF para múltiples condiciones.
Escrito por Eduardo Lázaro
