Sentencia CASE
La sentencia CASE proporciona una forma limpia de manejar múltiples condiciones. Hay dos formas: el CASE simple que compara un valor con opciones, y el CASE buscado que evalúa condiciones booleanas.
Mientras que IF es ideal para bifurcaciones de dos o tres caminos, CASE brilla cuando necesitas clasificar un valor entre muchas opciones posibles. Es más legible que una cadena larga de ELSEIF y comunica mejor la intención: "toma este valor y decide según su contenido". No confundas la sentencia CASE del procedimiento con la expresión CASE que se usa en SELECTs.
CASE simple
El CASE simple compara una expresión con valores concretos. MySQL evalúa la expresión una vez y la compara con cada WHEN en orden. Ejecuta el bloque del primer WHEN que coincida:
CASE expresion
WHEN valor1 THEN sentencias;
WHEN valor2 THEN sentencias;
...
ELSE sentencias;
END CASE;Es especialmente útil cuando tienes una variable con un conjunto limitado de valores posibles (estados, categorías, tipos, roles) y necesitas ejecutar lógica diferente para cada uno:
DELIMITER //
CREATE PROCEDURE estado_pedido(IN ped_id INT)
BEGIN
DECLARE v_estado VARCHAR(20);
DECLARE v_descripcion VARCHAR(100);
SELECT estado INTO v_estado FROM pedidos WHERE id = ped_id;
CASE v_estado
WHEN 'pendiente' THEN
SET v_descripcion = 'El pedido está esperando ser procesado';
WHEN 'enviado' THEN
SET v_descripcion = 'El pedido está en camino';
WHEN 'entregado' THEN
SET v_descripcion = 'El pedido ha sido entregado';
WHEN 'cancelado' THEN
SET v_descripcion = 'El pedido fue cancelado';
ELSE
SET v_descripcion = 'Estado desconocido';
END CASE;
SELECT ped_id AS pedido, v_estado AS estado, v_descripcion AS descripcion;
END //
DELIMITER ;CALL estado_pedido(1);| pedido | estado | descripcion |
|---|---|---|
| 1 | entregado | El pedido ha sido entregado |
CALL estado_pedido(23);| pedido | estado | descripcion |
|---|---|---|
| 23 | pendiente | El pedido está esperando ser procesado |
La legibilidad es inmediata: al leer CASE v_estado, cualquier desarrollador entiende que se está evaluando el estado del pedido. Con un IF equivalente (IF v_estado = 'pendiente' THEN ... ELSEIF v_estado = 'enviado' THEN ...), la variable se repite en cada condición, añadiendo ruido visual sin aportar información.
CASE buscado
El CASE buscado no compara un valor con opciones, sino que evalúa condiciones booleanas independientes. Cada WHEN puede contener cualquier expresión que devuelva verdadero o falso, lo que lo hace más flexible que el CASE simple:
CASE
WHEN condicion1 THEN sentencias;
WHEN condicion2 THEN sentencias;
...
ELSE sentencias;
END CASE;Es la opción adecuada cuando las condiciones involucran rangos, comparaciones con diferentes variables o expresiones complejas que no se reducen a comparar un solo valor con opciones:
DELIMITER //
CREATE PROCEDURE clasificar_cliente(IN cli_id INT)
BEGIN
DECLARE v_nombre VARCHAR(100);
DECLARE v_total_pedidos INT;
DECLARE v_total_gastado DECIMAL(10,2);
DECLARE v_clasificacion VARCHAR(20);
SELECT nombre INTO v_nombre FROM clientes WHERE id = cli_id;
SELECT COUNT(*), COALESCE(SUM(total), 0)
INTO v_total_pedidos, v_total_gastado
FROM pedidos WHERE cliente_id = cli_id;
CASE
WHEN v_total_gastado > 5000 THEN
SET v_clasificacion = 'VIP';
WHEN v_total_gastado > 2000 THEN
SET v_clasificacion = 'Premium';
WHEN v_total_gastado > 500 THEN
SET v_clasificacion = 'Regular';
WHEN v_total_pedidos > 0 THEN
SET v_clasificacion = 'Nuevo';
ELSE
SET v_clasificacion = 'Sin compras';
END CASE;
SELECT v_nombre AS cliente,
v_total_pedidos AS pedidos,
v_total_gastado AS total_gastado,
v_clasificacion AS clasificacion;
END //
DELIMITER ;CALL clasificar_cliente(1);| cliente | pedidos | total_gastado | clasificacion |
|---|---|---|---|
| María | 3 | 3899.97 | Premium |
Al igual que con IF...ELSEIF, el orden de las condiciones en un CASE buscado importa. MySQL ejecuta la primera que sea verdadera, así que las condiciones más restrictivas deben ir primero. Un cliente con 6000 gastado cumple tanto > 5000 como > 2000, pero al estar > 5000 primero, queda clasificado correctamente como VIP.
CASE con múltiples sentencias
Cada rama WHEN puede contener varias sentencias, no solo un SET. Esto permite ejecutar operaciones complejas en cada caso:
DELIMITER //
CREATE PROCEDURE procesar_devolucion(IN ped_id INT)
BEGIN
DECLARE v_estado VARCHAR(20);
DECLARE v_mensaje VARCHAR(200);
SELECT estado INTO v_estado FROM pedidos WHERE id = ped_id;
CASE v_estado
WHEN 'entregado' THEN
UPDATE pedidos SET estado = 'cancelado' WHERE id = ped_id;
SET v_mensaje = 'Devolución procesada correctamente';
WHEN 'enviado' THEN
UPDATE pedidos SET estado = 'cancelado' WHERE id = ped_id;
SET v_mensaje = 'Envío interceptado y cancelado';
WHEN 'pendiente' THEN
UPDATE pedidos SET estado = 'cancelado' WHERE id = ped_id;
SET v_mensaje = 'Pedido cancelado antes del envío';
WHEN 'cancelado' THEN
SET v_mensaje = 'El pedido ya estaba cancelado';
ELSE
SET v_mensaje = 'No se puede procesar la devolución';
END CASE;
SELECT v_mensaje AS resultado;
END //
DELIMITER ;Observa que las tres primeras ramas ejecutan un UPDATE además del SET. El CASE agrupa la lógica de forma que queda claro qué acciones se toman para cada estado, algo que con IF anidados sería más difícil de seguir visualmente.
CASE simple vs CASE buscado
La elección entre las dos formas depende de la naturaleza de las condiciones:
| Característica | CASE simple | CASE buscado |
|---|---|---|
| Compara | Un valor con opciones | Condiciones booleanas |
| Flexibilidad | Menor, solo igualdad | Mayor, cualquier condición |
| Legibilidad | Más limpio para valores discretos | Mejor para rangos |
| Rendimiento | Similar | Similar |
Usa el CASE simple cuando evalúes un valor contra un conjunto finito de opciones (estados, tipos, categorías). Usa el CASE buscado cuando necesites rangos numéricos, comparaciones entre diferentes variables, o condiciones que no se reducen a una simple igualdad.
CASE vs IF
Tanto CASE como IF pueden resolver los mismos problemas, pero cada uno tiene escenarios donde resulta más natural:
| Escenario | Recomendación |
|---|---|
| Valor con múltiples opciones discretas | CASE simple |
| Rangos numéricos | CASE buscado o IF |
| Solo dos opciones | IF...ELSE |
| Lógica compleja con anidamiento | IF |
| Muchas ramas con la misma variable | CASE simple |
En general, si te encuentras escribiendo IF variable = 'valor1' THEN ... ELSEIF variable = 'valor2' THEN ... con más de tres opciones, el código será más legible con un CASE simple.
Importante: ELSE en CASE
A diferencia de IF, donde el ELSE es opcional, en la sentencia CASE omitir el ELSE puede generar un error en tiempo de ejecución. Si ninguna condición WHEN coincide y no hay ELSE, MySQL genera el error 1339:
-- Esto genera error si v_estado no coincide con ningún WHEN
CASE v_estado
WHEN 'activo' THEN SET x = 1;
WHEN 'inactivo' THEN SET x = 0;
-- Sin ELSE: Error 1339 - Case not found for CASE statement
END CASE;Siempre incluye una cláusula ELSE en tus sentencias CASE, aunque sea con una acción vacía (ELSE BEGIN END) o un SET a un valor por defecto. Esta es una diferencia importante con respecto a la expresión CASE que se usa en SELECTs, donde la ausencia de ELSE simplemente devuelve NULL.
Limpieza
DROP PROCEDURE IF EXISTS estado_pedido;
DROP PROCEDURE IF EXISTS clasificar_cliente;
DROP PROCEDURE IF EXISTS procesar_devolucion;En el siguiente artículo veremos el bucle LOOP para repetir sentencias dentro de un procedimiento.
Escrito por Eduardo Lázaro
