SUM IF
En el artículo anterior vimos cómo SUM calcula el total de una columna numérica. Ahora vamos un paso más allá: sumar solo los valores que cumplen una condición determinada. Este patrón, conocido como SUM IF, te permite calcular múltiples sumas condicionales en una sola consulta, lo que resulta ideal para construir informes de tipo pivote, comparar períodos y segmentar datos sin recurrir a subconsultas.
Sintaxis
-- Patrón con IF
SUM(IF(condicion, expresion, 0))
-- Patrón con CASE (más portable)
SUM(CASE WHEN condicion THEN expresion ELSE 0 END)Ambos patrones son equivalentes. IF es más conciso y específico de MySQL, mientras que CASE funciona en cualquier base de datos SQL. En ambos casos, cuando la condición no se cumple, la expresión devuelve 0, que al sumarse no afecta al total. También podrías usar NULL en lugar de 0 ya que SUM ignora los NULL, pero usar 0 hace la intención más explícita.
Comportamiento básico
Supongamos que quieres saber cuánto se ha facturado en pedidos completados frente a pedidos cancelados:
SELECT
SUM(IF(estado = 'completado', total, 0)) AS ingresos_completados,
SUM(IF(estado = 'cancelado', total, 0)) AS ingresos_cancelados,
SUM(total) AS ingresos_brutos
FROM pedidos;| ingresos_completados | ingresos_cancelados | ingresos_brutos |
|---|---|---|
| 178340.55 | 14520.30 | 215680.85 |
De los 215680.85 euros en pedidos brutos, 178340.55 corresponden a pedidos completados y 14520.30 a cancelados. La diferencia entre los ingresos brutos y la suma de completados más cancelados corresponde a pedidos en otros estados (pendiente, procesando, enviado).
Caso práctico: comparación de ingresos por trimestre
Uno de los usos más potentes de SUM IF es comparar períodos en la misma fila. Veamos los ingresos por categoría desglosados por trimestre:
SELECT
c.nombre AS categoria,
SUM(CASE WHEN QUARTER(p.fecha_pedido) = 1
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) AS Q1,
SUM(CASE WHEN QUARTER(p.fecha_pedido) = 2
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) AS Q2,
SUM(CASE WHEN QUARTER(p.fecha_pedido) = 3
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) AS Q3,
SUM(CASE WHEN QUARTER(p.fecha_pedido) = 4
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) AS Q4
FROM pedidos p
JOIN detalle_pedidos dp ON p.id = dp.pedido_id
JOIN productos pr ON dp.producto_id = pr.id
JOIN categorias c ON pr.categoria_id = c.id
WHERE YEAR(p.fecha_pedido) = 2025
AND p.estado = 'completado'
GROUP BY c.nombre
ORDER BY c.nombre;| categoria | Q1 | Q2 | Q3 | Q4 |
|---|---|---|---|---|
| Accesorios electrónicos | 5230.40 | 6120.80 | 6580.20 | 6628.90 |
| Deportes | 2340.00 | 2890.50 | 1980.00 | 3120.00 |
| Hogar | 1890.30 | 2150.60 | 1650.40 | 2450.80 |
| Portátiles | 12450.00 | 14230.50 | 13100.00 | 15639.50 |
| Ropa hombre | 2560.20 | 2980.00 | 1870.40 | 3149.80 |
| Ropa mujer | 3120.50 | 3450.20 | 2680.00 | 3999.90 |
| Smartphones | 16890.30 | 18560.40 | 19230.00 | 19549.80 |
Esta tabla pivote te permite ver de un vistazo las tendencias de cada categoría a lo largo del año. Los Smartphones muestran un crecimiento sostenido trimestre a trimestre, mientras que Deportes tiene una caída en el tercer trimestre que probablemente corresponde al verano.
Caso práctico: informe de ventas actual vs anterior
Otro patrón muy habitual es comparar el año actual con el anterior en la misma consulta:
SELECT
DATE_FORMAT(p.fecha_pedido, '%m') AS mes,
SUM(CASE WHEN YEAR(p.fecha_pedido) = 2025
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) AS ingresos_2025,
SUM(CASE WHEN YEAR(p.fecha_pedido) = 2024
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) AS ingresos_2024,
ROUND(
(SUM(CASE WHEN YEAR(p.fecha_pedido) = 2025
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) -
SUM(CASE WHEN YEAR(p.fecha_pedido) = 2024
THEN dp.cantidad * dp.precio_unitario ELSE 0 END)) /
NULLIF(SUM(CASE WHEN YEAR(p.fecha_pedido) = 2024
THEN dp.cantidad * dp.precio_unitario ELSE 0 END), 0) * 100
, 1) AS variacion_pct
FROM pedidos p
JOIN detalle_pedidos dp ON p.id = dp.pedido_id
WHERE p.estado = 'completado'
AND MONTH(p.fecha_pedido) <= 6
GROUP BY DATE_FORMAT(p.fecha_pedido, '%m')
ORDER BY mes;| mes | ingresos_2025 | ingresos_2024 | variacion_pct |
|---|---|---|---|
| 01 | 18450.75 | 15680.20 | 17.7 |
| 02 | 21320.40 | 18230.50 | 16.9 |
| 03 | 25890.25 | 22150.80 | 16.9 |
| 04 | 19870.60 | 17450.30 | 13.9 |
| 05 | 24150.80 | 20890.40 | 15.6 |
| 06 | 29560.50 | 24560.00 | 20.4 |
La columna variacion_pct calcula el porcentaje de crecimiento interanual. NULLIF en el denominador evita la división por cero en caso de que un mes no tuviera ventas en 2024.
Caso práctico: tabla pivote de métodos de pago
SUM IF es perfecto para crear tablas pivote que transforman filas en columnas:
SELECT
cl.ciudad,
SUM(CASE WHEN p.metodo_pago = 'tarjeta' THEN p.total ELSE 0 END) AS tarjeta,
SUM(CASE WHEN p.metodo_pago = 'transferencia' THEN p.total ELSE 0 END) AS transferencia,
SUM(CASE WHEN p.metodo_pago = 'paypal' THEN p.total ELSE 0 END) AS paypal,
SUM(p.total) AS total_ciudad
FROM pedidos p
JOIN clientes cl ON p.cliente_id = cl.id
WHERE p.estado = 'completado'
GROUP BY cl.ciudad
ORDER BY total_ciudad DESC
LIMIT 5;| ciudad | tarjeta | transferencia | paypal | total_ciudad |
|---|---|---|---|---|
| Madrid | 28450.30 | 12560.20 | 8920.00 | 49930.50 |
| Barcelona | 22340.80 | 9870.40 | 7650.20 | 39861.40 |
| Valencia | 14560.50 | 6230.00 | 4890.30 | 25680.80 |
| Sevilla | 11230.20 | 5120.60 | 3450.00 | 19800.80 |
| Bilbao | 8920.40 | 4230.50 | 2890.20 | 16041.10 |
Cada fila muestra cómo se distribuyen los pagos en cada ciudad. Este tipo de informe sería muy difícil de generar sin el patrón SUM IF.
Manejo de NULL
Cuando la expresión dentro de SUM IF puede ser NULL, hay que tener cuidado. Si usas ELSE 0 en el CASE, los valores que no cumplen la condición se convierten en cero. Pero si la propia expresión que sumas es NULL, el resultado de toda la rama THEN será NULL:
SELECT
SUM(CASE WHEN categoria_id = 1
THEN descuento ELSE 0 END) AS descuento_electronica
FROM productos;Si algún producto de electrónica tiene descuento como NULL, esa fila aportará NULL a la suma, que será ignorado por SUM. Esto generalmente es el comportamiento deseado, pero si quieres contar los NULL como cero, usa IFNULL:
SELECT
SUM(CASE WHEN categoria_id = 1
THEN IFNULL(descuento, 0) ELSE 0 END) AS descuento_electronica
FROM productos;Combinación con otras funciones
Puedes anidar SUM IF dentro de otras expresiones para cálculos más sofisticados:
SELECT
c.nombre AS categoria,
SUM(dp.cantidad * dp.precio_unitario) AS ingresos_totales,
SUM(CASE WHEN p.estado = 'completado'
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) AS ingresos_netos,
ROUND(
SUM(CASE WHEN p.estado = 'completado'
THEN dp.cantidad * dp.precio_unitario ELSE 0 END) /
SUM(dp.cantidad * dp.precio_unitario) * 100
, 1) AS tasa_conversion
FROM pedidos p
JOIN detalle_pedidos dp ON p.id = dp.pedido_id
JOIN productos pr ON dp.producto_id = pr.id
JOIN categorias c ON pr.categoria_id = c.id
GROUP BY c.nombre
HAVING SUM(dp.cantidad * dp.precio_unitario) > 10000
ORDER BY tasa_conversion DESC;| categoria | ingresos_totales | ingresos_netos | tasa_conversion |
|---|---|---|---|
| Accesorios electrónicos | 28750.40 | 24560.30 | 85.4 |
| Smartphones | 87450.25 | 74230.50 | 84.9 |
| Portátiles | 65320.80 | 55420.00 | 84.8 |
| Ropa hombre | 12450.90 | 10560.40 | 84.8 |
| Ropa mujer | 15680.20 | 13250.60 | 84.5 |
La tasa de conversión muestra qué porcentaje de los ingresos brutos se materializó en ventas completadas. Es una métrica valiosa para entender la salud de cada línea de producto.
En el siguiente artículo veremos AVG para calcular promedios.
Escrito por Eduardo Lázaro
