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
ENDLa forma buscada evalúa condiciones independientes:
CASE
WHEN condicion1 THEN resultado1
WHEN condicion2 THEN resultado2
...
ELSE resultado_por_defecto
ENDEn 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;| id | fecha_pedido | estado | estado_detallado |
|---|---|---|---|
| 1 | 2025-10-15 09:30:00 | entregado | Entregado al cliente |
| 2 | 2025-10-18 14:22:00 | entregado | Entregado al cliente |
| 3 | 2025-10-20 11:05:00 | entregado | Entregado al cliente |
| 12 | 2025-12-01 10:30:00 | procesando | En preparación |
| 13 | 2025-12-03 14:00:00 | pendiente | Pendiente de pago |
| 16 | 2025-12-10 16:00:00 | cancelado | Cancelado |
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;| nombre | precio | segmento |
|---|---|---|
| iPhone 15 Pro | 1299.99 | Premium |
| Samsung Galaxy S24 | 899.99 | Gama media |
| Google Pixel 8 | 699.00 | Gama media |
| Xiaomi 14 | 599.99 | Gama media |
| MacBook Air M3 | 1399.00 | Premium |
| Funda iPhone silicona | 49.99 | Accesorio |
| Cargador USB-C 65W | 35.99 | Accesorio |
| Cable USB-C a Lightning | 19.99 | Accesorio |
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;| empleado | fecha_contratacion | anos | nivel |
|---|---|---|---|
| Laura Martínez Díaz | 2019-03-15 | 6 | Senior |
| Carlos Gómez Ruiz | 2020-01-10 | 6 | Senior |
| Ana López Torres | 2021-06-01 | 4 | Consolidado |
| Pedro Sánchez Mora | 2022-09-20 | 3 | Consolidado |
| Lucía Fernández Gil | 2024-02-14 | 1 | Junior |
| Daniel Herrera Vega | 2024-08-01 | 1 | Junior |
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;| id | estado | total | fecha_pedido |
|---|---|---|---|
| 13 | pendiente | 39.99 | 2025-12-03 14:00:00 |
| 14 | pendiente | 79.98 | 2025-12-05 11:15:00 |
| 15 | pendiente | 1549.00 | 2025-12-08 09:30:00 |
| 18 | pendiente | 329.98 | 2025-12-15 10:00:00 |
| 12 | procesando | 199.99 | 2025-12-01 10:30:00 |
| 5 | enviado | 1899.99 | 2025-10-25 16:45:00 |
| 7 | enviado | 549.00 | 2025-11-01 09:15:00 |
| 9 | enviado | 129.99 | 2025-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;| trimestre | entregados | enviados | pendientes | cancelados |
|---|---|---|---|---|
| 1 | 0.00 | 0.00 | 94.97 | 0.00 |
| 4 | 5059.93 | 3578.97 | 2058.94 | 748.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;| trimestre | n_entregados | n_cancelados |
|---|---|---|
| 1 | 0 | 0 |
| 4 | 5 | 3 |
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;| nombre | telefono_secundario | contacto_alternativo |
|---|---|---|
| María | NULL | Sin teléfono alternativo |
| Carlos | 612345678 | 612345678 |
| Ana | NULL | Sin teléfono alternativo |
| Pedro | 698765432 | 698765432 |
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;| nombre | stock | alerta_inventario |
|---|---|---|
| Sofá 3 plazas | 8 | Stock bajo: 8 uds. - Revisar proveedor |
| ASUS ROG Zephyrus | 12 | Stock bajo: 12 uds. - Revisar proveedor |
| Escritorio ajust. | 15 | Stock bajo: 15 uds. - Revisar proveedor |
| Lenovo ThinkPad X1 | 18 | Stock bajo: 18 uds. - Revisar proveedor |
| Google Pixel 8 | 38 | OK (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:
En el siguiente artículo veremos IF como alternativa más concisa para condiciones simples.
Escrito por Eduardo Lázaro
