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);
pedidoestadodescripcion
1entregadoEl pedido ha sido entregado
CALL estado_pedido(23);
pedidoestadodescripcion
23pendienteEl 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);
clientepedidostotal_gastadoclasificacion
María33899.97Premium

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ísticaCASE simpleCASE buscado
ComparaUn valor con opcionesCondiciones booleanas
FlexibilidadMenor, solo igualdadMayor, cualquier condición
LegibilidadMás limpio para valores discretosMejor para rangos
RendimientoSimilarSimilar

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:

EscenarioRecomendación
Valor con múltiples opciones discretasCASE simple
Rangos numéricosCASE buscado o IF
Solo dos opcionesIF...ELSE
Lógica compleja con anidamientoIF
Muchas ramas con la misma variableCASE 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