Seguro que ya te has topado con expresiones condicionales en otros lenguajes de programación: if-else, switch-case y cosas así. En SQL tenemos nuestra propia herramienta para trabajar con condiciones: la expresión CASE. Te deja tomar decisiones directamente en las consultas: si la condición se cumple, haces una cosa, si no, otra.
La construcción CASE es especialmente útil cuando tienes que trabajar con datos que pueden tener valores NULL. El sintaxis es súper simple, como una chuleta, mira:
CASE
WHEN condición1 THEN resultado1
WHEN condición2 THEN resultado2
...
ELSE resultado_por_defecto
END
¿A que suena lógico? "Si pasa esto, haz esto, si no, haz lo otro". Es fácil de recordar: "WHEN es el si, THEN es lo que haces, ELSE es lo que haces si nada encaja".
Ejemplo: clasificación de productos
Imagina que tenemos una tabla products, con una columna price, y queremos agrupar los productos según su precio.
| id | name | price |
|---|---|---|
| 1 | Magic Wand | 120 |
| 2 | Potion Set | 45 |
| 3 | Crystal Ball | 75 |
| 4 | Enchanted Map | NULL |
| 5 | Broomstick | 99 |
| 6 | Spell Book | 180 |
SELECT
name AS product_name,
price,
CASE
WHEN price IS NULL THEN 'Desconocido'
WHEN price < 50 THEN 'Económico'
WHEN price BETWEEN 50 AND 100 THEN 'Estándar'
ELSE 'Premium'
END AS price_category
FROM products;
¿Qué pasa aquí?
- Si el precio
pricedel producto no está puesto (NULL), mostramos la categoría como'Desconocido'. - Si el precio es menor que 50, el producto es económico
'Económico'. - Si el precio está entre 50 y 100, va a la categoría
'Estándar'. - Todo lo demás son productos premium
'Premium'.
Así queda el resultado:
| product_name | price | price_category |
|---|---|---|
| Magic Wand | 120 | Premium |
| Potion Set | 45 | Económico |
| Crystal Ball | 75 | Estándar |
| Enchanted Map | NULL | Desconocido |
| Broomstick | 99 | Estándar |
| Spell Book | 180 | Premium |
SQL, como un mago, lee las filas de la tabla products, las mira una a una y las clasifica con arte.
Trabajando con NULL en CASE WHEN
Muchas veces hay valores que faltan en los datos (hola, NULL), y hay que ponerles algo. Por ejemplo, tenemos una tabla users con una columna email, y para los usuarios sin correo queremos mostrar 'No especificado'.
| user_id | name | |
|---|---|---|
| 1 | Alex Lin | alex@example.com |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | anna@magic.com |
| 4 | Otto Art | NULL |
| 5 | John Smith | john@wizard.org |
SELECT
user_id,
name,
CASE
WHEN email IS NULL THEN 'No especificado'
ELSE email
END AS email_address
FROM users;
| user_id | name | email_address |
|---|---|---|
| 1 | Alex Lin | alex@example.com |
| 2 | Maria Chi | No especificado |
| 3 | Anna Song | anna@magic.com |
| 4 | Otto Art | No especificado |
| 5 | John Smith | john@wizard.org |
SQL aquí actúa como un chamán, rellenando las filas medio vacías. Si falta el email, lo cambia por 'No especificado'. Si hay email, lo deja tal cual.
Expresiones condicionales con números
A veces no solo hay que cambiar valores, sino crear lógica nueva. Por ejemplo, imagina que tenemos una tabla students con columnas score (nota) y name.
| name | score |
|---|---|
| Alex Lin | 95 |
| Maria Chi | 82 |
| Anna Song | 48 |
| Otto Art | NULL |
| John Smith | 67 |
| Zoe Black | 30 |
Queremos evaluar a los estudiantes como "Excelente", "Aprobado" y "Suspendido", según sus notas.
SELECT
name AS student_name,
score,
CASE
WHEN score IS NULL THEN 'Sin nota'
WHEN score >= 90 THEN 'Excelente'
WHEN score >= 50 THEN 'Aprobado'
ELSE 'Suspendido'
END AS performance_category
FROM students;
| student_name | score | performance_category |
|---|---|---|
| Alex Lin | 95 | Excelente |
| Maria Chi | 82 | Aprobado |
| Anna Song | 48 | Suspendido |
| Otto Art | NULL | Sin nota |
| John Smith | 67 | Aprobado |
| Zoe Black | 30 | Suspendido |
SQL reparte a los estudiantes como un profe exigente: 90 puntos o más — solo "Excelente", desde 50 — "Aprobado". Si no llega... ya sabes.
Agrupación y manejo de NULL
Trabajar con grupos de datos es otra área donde CASE WHEN brilla. Imagina que tenemos una tabla orders, y queremos contar el total de pedidos por estado, incluyendo los que tienen NULL como estado.
| order_id | status |
|---|---|
| 1 | Completed |
| 2 | Pending |
| 3 | NULL |
| 4 | Shipped |
| 5 | Completed |
| 6 | NULL |
| 7 | Pending |
| 8 | Completed |
| 9 | Shipped |
| 10 | NULL |
SELECT
CASE
WHEN status IS NULL THEN 'Sin estado'
ELSE status
END AS order_status,
COUNT(*)
FROM orders
GROUP BY
CASE
WHEN status IS NULL THEN 'Sin estado'
ELSE status
END;
Esta consulta maneja los estados vacíos NULL y pone 'Sin estado' en su lugar, además de contar el total de pedidos en cada grupo. Así queda:
| order_status | count |
|---|---|
| Completed | 3 |
| Pending | 2 |
| Shipped | 2 |
| Sin estado | 3 |
Casos prácticos: "Trucos" con CASE WHEN
Ejemplo 1: Ordenar teniendo en cuenta NULL
A veces necesitas que los valores NULL salgan primero o al final en una lista ordenada. Esto se usa mucho, por ejemplo, en listas de tareas, donde las tareas prioritarias deben ir arriba y las que no tienen prioridad (NULL) al final.
| task_id | task_name | priority |
|---|---|---|
| 1 | Fix bugs | 1 |
| 2 | Update documentation | 3 |
| 3 | Plan sprint | NULL |
| 4 | Code review | 2 |
| 5 | Organize meeting | NULL |
| 6 | Deploy release | 1 |
SELECT
task_name,
priority,
CASE
WHEN priority IS NULL THEN 1
ELSE 0
END AS priority_sort
FROM tasks
ORDER BY priority_sort ASC, priority ASC;
Aquí hemos añadido una columna "virtual" priority_sort, que pone los NULL al final y el resto los ordena de menor a mayor.
| task_name | priority | priority_sort |
|---|---|---|
| Deploy release | 1 | 0 |
| Fix bugs | 1 | 0 |
| Code review | 2 | 0 |
| Update documentation | 3 | 0 |
| Plan sprint | NULL | 1 |
| Organize meeting | NULL | 1 |
Ejemplo 2: Cálculos teniendo en cuenta NULL
Ahora imagina que calculamos el precio final de un pedido en la tabla orders, donde la columna discount puede ser NULL si no hay descuento.
| order_id | total_price | discount |
|---|---|---|
| 101 | 100 | 10 |
| 102 | 200 | NULL |
| 103 | 150 | 15 |
| 104 | 120 | NULL |
| 105 | 80 | 5 |
Tenemos que cambiar NULL por 0 para que el cálculo no se rompa.
SELECT
order_id,
total_price,
discount,
total_price -
CASE
WHEN discount IS NULL THEN 0
ELSE discount
END AS final_price
FROM orders;
| order_id | total_price | discount | final_price |
|---|---|---|---|
| 101 | 100 | 10 | 90 |
| 102 | 200 | NULL | 200 |
| 103 | 150 | 15 | 135 |
| 104 | 120 | NULL | 120 |
| 105 | 80 | 5 | 75 |
Este truco mágico con CASE nos asegura que NULL no fastidie los cálculos.
Para orderid = 101: discount = 10. finalprice = 100 - 10 = 90. Para orderid = 102: discount = NULL. CASE devuelve 0. finalprice = 200 - 0 = 200. Para orderid = 103: discount = 15. finalprice = 150 - 15 = 135. Para orderid = 104: discount = NULL. CASE devuelve 0. finalprice = 120 - 0 = 120. Para orderid = 105: discount = 5. finalprice = 80 - 5 = 75.
Ejemplo 3: Mostrar estados de usuario personalizados
En el día a día suele haber que mostrar el estado del usuario (por ejemplo, "Activo" o "Pendiente") o indicar que no hay datos. Por ejemplo, en la tabla users hay una columna last_login con la fecha del último acceso.
| user_id | name | last_login |
|---|---|---|
| 1 | Alex Lin | 2024-12-10 |
| 2 | Maria Chi | 2025-04-20 |
| 3 | Anna Song | NULL |
| 4 | Otto Art | 2025-05-01 |
| 5 | Liam Park | 2025-05-25 |
SELECT
user_id,
name,
CASE
WHEN last_login IS NULL THEN 'Nunca ha iniciado sesión'
WHEN last_login < CURRENT_DATE - INTERVAL '30 days' THEN 'Inactivo'
ELSE 'Activo'
END AS user_status
FROM users;
Con esta consulta el sistema de admin cobra vida: los que nunca han entrado son "Nunca ha iniciado sesión", los que llevan mucho sin entrar son "Inactivo". ¡El resto están activos!
| user_id | name | user_status |
|---|---|---|
| 1 | Alex Lin | Inactivo |
| 2 | Maria Chi | Inactivo |
| 3 | Anna Song | Nunca ha iniciado sesión |
| 4 | Otto Art | Inactivo |
| 5 | Liam Park | Activo |
Errores comunes y cómo evitarlos
Olvidar ELSE: Si no pones ELSE, SQL simplemente devuelve NULL si ninguna condición se cumple. No siempre es lo que quieres. Así que mejor pon ELSE explícitamente, aunque creas que has cubierto todos los casos.
CASE
WHEN condition THEN result
-- ELSE 'Valor por defecto' -- ¡No lo olvides!
END
Condiciones complejas sin paréntesis: Si tienes varias condiciones con AND, OR o NOT, usa siempre paréntesis. Sin ellos tu consulta puede "pensar" mal.
CASE
WHEN (column1 IS NOT NULL AND column2 > 5) THEN 'Válido'
ELSE 'Inválido'
END
Trabajando con NULL: Recuerda que NULL nunca es igual (=) a nada. Por ejemplo:
CASE
WHEN column = NULL THEN '¡Nope!' -- ¡Error!
WHEN column IS NULL THEN '¡Correcto!' -- Así sí.
END
GO TO FULL VERSION