CASE

La expresión CASE permite introducir lógica condicional directamente en una consulta SQL. Funciona de manera similar a las estructuras if/else o switch de los lenguajes de programación, pero se evalúa dentro del propio motor de la base de datos. Esto significa que puedes clasificar, transformar o decidir valores sin necesidad de procesar los datos en tu aplicación.

MySQL ofrece dos formas de CASE: la forma simple, que compara una expresión contra valores concretos, y la forma buscada, que evalúa condiciones booleanas independientes. Ambas devuelven un valor por cada fila procesada.

Sintaxis

La forma simple compara una expresión contra varios valores posibles:

CASE expresion
    WHEN valor1 THEN resultado1
    WHEN valor2 THEN resultado2
    ...
    ELSE resultado_por_defecto
END

La forma buscada evalúa condiciones independientes:

CASE
    WHEN condicion1 THEN resultado1
    WHEN condicion2 THEN resultado2
    ...
    ELSE resultado_por_defecto
END

En ambas formas, MySQL evalúa las cláusulas WHEN en orden de aparición y devuelve el resultado de la primera que se cumpla. Si ninguna se cumple, devuelve el valor del ELSE. Si no hay ELSE y ninguna condición coincide, el resultado es NULL.

Comportamiento básico

La forma simple es ideal cuando comparas una sola columna contra valores fijos. Supón que la tabla pedidos tiene una columna estado con valores como pendiente, procesando, enviado, entregado y cancelado. Puedes traducir esos valores a etiquetas más descriptivas:

SELECT
    id,
    fecha_pedido,
    estado,
    CASE estado
        WHEN 'pendiente'   THEN 'Pendiente de pago'
        WHEN 'procesando'  THEN 'En preparación'
        WHEN 'enviado'     THEN 'En camino'
        WHEN 'entregado'   THEN 'Entregado al cliente'
        WHEN 'cancelado'   THEN 'Cancelado'
    END AS estado_detallado
FROM pedidos
LIMIT 6;
idfecha_pedidoestadoestado_detallado
12025-10-15 09:30:00entregadoEntregado al cliente
22025-10-18 14:22:00entregadoEntregado al cliente
32025-10-20 11:05:00entregadoEntregado al cliente
122025-12-01 10:30:00procesandoEn preparación
132025-12-03 14:00:00pendientePendiente de pago
162025-12-10 16:00:00canceladoCancelado

La expresión CASE estado evalúa el valor de la columna estado y lo compara contra cada WHEN. Cuando encuentra una coincidencia, devuelve el texto correspondiente.

La forma buscada es más flexible porque permite evaluar condiciones con cualquier operador. Para clasificar productos por rango de precio:

SELECT
    nombre,
    precio,
    CASE
        WHEN precio >= 1000 THEN 'Premium'
        WHEN precio >= 500  THEN 'Gama media'
        WHEN precio >= 100  THEN 'Económico'
        ELSE 'Accesorio'
    END AS segmento
FROM productos
LIMIT 8;
nombrepreciosegmento
iPhone 15 Pro1299.99Premium
Samsung Galaxy S24899.99Gama media
Google Pixel 8699.00Gama media
Xiaomi 14599.99Gama media
MacBook Air M31399.00Premium
Funda iPhone silicona49.99Accesorio
Cargador USB-C 65W35.99Accesorio
Cable USB-C a Lightning19.99Accesorio

Aquí el orden de las condiciones importa. Un producto de 1200 euros cumple tanto precio >= 1000 como precio >= 500, pero MySQL devuelve el resultado de la primera condición que se cumpla. Si invirtieras el orden, todos los productos de 1000 euros o más se clasificarían como "Gama media" en lugar de "Premium".

Caso práctico: clasificar empleados por antigüedad

Puedes usar CASE con funciones de fecha para calcular la antigüedad de los empleados y asignarles una categoría:

SELECT
    CONCAT(nombre, ' ', apellidos) AS empleado,
    fecha_contratacion,
    TIMESTAMPDIFF(YEAR, fecha_contratacion, CURDATE()) AS anos,
    CASE
        WHEN TIMESTAMPDIFF(YEAR, fecha_contratacion, CURDATE()) >= 5 THEN 'Senior'
        WHEN TIMESTAMPDIFF(YEAR, fecha_contratacion, CURDATE()) >= 2 THEN 'Consolidado'
        ELSE 'Junior'
    END AS nivel
FROM empleados
ORDER BY fecha_contratacion;
empleadofecha_contratacionanosnivel
Laura Martínez Díaz2019-03-156Senior
Carlos Gómez Ruiz2020-01-106Senior
Ana López Torres2021-06-014Consolidado
Pedro Sánchez Mora2022-09-203Consolidado
Lucía Fernández Gil2024-02-141Junior
Daniel Herrera Vega2024-08-011Junior

La función TIMESTAMPDIFF(YEAR, ...) calcula la diferencia en años entre la fecha de contratación y la fecha actual. El CASE clasifica a los empleados según ese cálculo. Este patrón es muy útil para generar reportes donde necesitas agrupar datos continuos en categorías discretas.

Caso práctico: ordenamiento personalizado con ORDER BY

Una aplicación potente de CASE es definir un orden de clasificación que no sigue el alfabético ni el numérico natural. Por ejemplo, si quieres que los pedidos aparezcan primero por urgencia de atención:

SELECT
    id,
    estado,
    total,
    fecha_pedido
FROM pedidos
ORDER BY
    CASE estado
        WHEN 'pendiente'   THEN 1
        WHEN 'procesando'  THEN 2
        WHEN 'enviado'     THEN 3
        WHEN 'entregado'   THEN 4
        WHEN 'cancelado'   THEN 5
    END,
    fecha_pedido ASC
LIMIT 8;
idestadototalfecha_pedido
13pendiente39.992025-12-03 14:00:00
14pendiente79.982025-12-05 11:15:00
15pendiente1549.002025-12-08 09:30:00
18pendiente329.982025-12-15 10:00:00
12procesando199.992025-12-01 10:30:00
5enviado1899.992025-10-25 16:45:00
7enviado549.002025-11-01 09:15:00
9enviado129.992025-11-10 11:30:00

El CASE asigna un número a cada estado, y ORDER BY utiliza esos números para ordenar. Los pedidos pendientes aparecen primero porque necesitan atención inmediata, seguidos de los que están en proceso. Dentro de cada grupo, se ordenan por fecha.

Caso práctico: CASE en UPDATE

La expresión CASE no se limita a consultas de lectura. Puedes usarla en sentencias UPDATE para aplicar diferentes cambios según una condición. Por ejemplo, para aplicar un descuento diferente a cada categoría de productos:

UPDATE productos
SET precio = precio * CASE
    WHEN categoria_id IN (6, 7) THEN 0.90    -- 10% descuento en smartphones y portátiles
    WHEN categoria_id = 8       THEN 0.85    -- 15% descuento en accesorios electrónicos
    ELSE 1.00                                -- sin cambios
END
WHERE categoria_id IN (6, 7, 8);

Este UPDATE modifica el precio de cada producto según su categoría, todo en una sola sentencia. Sin CASE, necesitarías ejecutar tres sentencias UPDATE separadas, lo que además de ser más código, es menos eficiente porque MySQL recorre la tabla tres veces.

Caso práctico: tabla pivote con CASE y funciones de agregación

Uno de los usos más avanzados de CASE es crear tablas pivote, donde conviertes valores de filas en columnas. Para ver el total de ventas por estado de pedido y por trimestre:

SELECT
    QUARTER(fecha_pedido) AS trimestre,
    SUM(CASE WHEN estado = 'entregado' THEN total ELSE 0 END)  AS entregados,
    SUM(CASE WHEN estado = 'enviado' THEN total ELSE 0 END)    AS enviados,
    SUM(CASE WHEN estado = 'pendiente' THEN total ELSE 0 END)  AS pendientes,
    SUM(CASE WHEN estado = 'cancelado' THEN total ELSE 0 END)  AS cancelados
FROM pedidos
GROUP BY QUARTER(fecha_pedido)
ORDER BY trimestre;
trimestreentregadosenviadospendientescancelados
10.000.0094.970.00
45059.933578.972058.94748.99

Cada SUM(CASE ...) actúa como un filtro: solo suma los totales de los pedidos cuyo estado coincide con la condición. El resultado es una tabla donde cada columna muestra el total acumulado para un estado específico, agrupado por trimestre.

También puedes contar pedidos en lugar de sumar importes. Para ello, sustituye SUM(... total ...) por COUNT(CASE WHEN ... THEN 1 END):

SELECT
    QUARTER(fecha_pedido) AS trimestre,
    COUNT(CASE WHEN estado = 'entregado' THEN 1 END) AS n_entregados,
    COUNT(CASE WHEN estado = 'cancelado' THEN 1 END) AS n_cancelados
FROM pedidos
GROUP BY QUARTER(fecha_pedido)
ORDER BY trimestre;
trimestren_entregadosn_cancelados
100
453

En esta variante el CASE devuelve 1 cuando la condición se cumple y NULL implícitamente cuando no (porque no hay ELSE). COUNT ignora los NULL, así que solo cuenta las filas que coinciden.

Manejo de NULL

La expresión CASE trata los valores NULL con una particularidad importante. En la forma simple, CASE expresion WHEN NULL THEN ... nunca se cumple, porque MySQL evalúa expresion = NULL, y cualquier comparación con NULL devuelve NULL (no verdadero). Para comprobar si un valor es NULL, debes usar la forma buscada:

SELECT
    nombre,
    telefono_secundario,
    CASE
        WHEN telefono_secundario IS NULL THEN 'Sin teléfono alternativo'
        ELSE telefono_secundario
    END AS contacto_alternativo
FROM clientes
LIMIT 4;
nombretelefono_secundariocontacto_alternativo
MaríaNULLSin teléfono alternativo
Carlos612345678612345678
AnaNULLSin teléfono alternativo
Pedro698765432698765432

Usa siempre WHEN ... IS NULL en la forma buscada para detectar nulos. La forma simple con WHEN NULL es un error frecuente que no produce errores de sintaxis pero tampoco funciona como se espera.

Combinación con otras funciones

CASE se combina bien con funciones de agregación, de cadena y de fecha. Por ejemplo, para generar un resumen de inventario con alertas:

SELECT
    nombre,
    stock,
    CASE
        WHEN stock = 0 THEN CONCAT('AGOTADO - Reabastecer antes de ', DATE_FORMAT(DATE_ADD(CURDATE(), INTERVAL 3 DAY), '%d/%m/%Y'))
        WHEN stock < 20 THEN CONCAT('Stock bajo: ', stock, ' uds. - Revisar proveedor')
        ELSE CONCAT('OK (', stock, ' uds.)')
    END AS alerta_inventario
FROM productos
WHERE stock < 50
ORDER BY stock ASC
LIMIT 5;
nombrestockalerta_inventario
Sofá 3 plazas8Stock bajo: 8 uds. - Revisar proveedor
ASUS ROG Zephyrus12Stock bajo: 12 uds. - Revisar proveedor
Escritorio ajust.15Stock bajo: 15 uds. - Revisar proveedor
Lenovo ThinkPad X118Stock bajo: 18 uds. - Revisar proveedor
Google Pixel 838OK (38 uds.)

En esta consulta, CASE decide qué mensaje generar y CONCAT construye cadenas dinámicas que incluyen el stock actual o fechas calculadas con DATE_ADD. Este tipo de combinación convierte consultas simples en reportes informativos que se pueden enviar directamente por email o mostrar en un dashboard.

También puedes anidar expresiones CASE dentro de otras, aunque esto reduce la legibilidad. Si necesitas más de dos niveles de anidación, considera dividir la lógica en una vista o en varias columnas calculadas.

Practica con CASE

Usa el editor para clasificar datos con expresiones CASE:

Simulador SQL
Ctrl+Enter para ejecutar

En el siguiente artículo veremos IF como alternativa más concisa para condiciones simples.

Escrito por Eduardo Lázaro