🎯 Situación
Un analista de BI me preguntó cómo clasificar clientes por ingresos dentro de cada región en SQL — mostrando el ranking de cada cliente junto a sus ingresos individuales y el total. Había intentado hacerlo con una subconsulta y un GROUP BY y había estado cerca pero no podía obtener el ranking por región correcto sin colapsar las filas de detalle. Una función de ventana reemplazó todo el enfoque de subconsulta y corrió 3x más rápido.
⚠️ El reto
📈 Las 4 familias de funciones de ventana
- Ranking: ROW_NUMBER(), RANK(), DENSE_RANK() — asignar un número secuencial a las filas dentro de una partición
- Agregación: SUM() OVER(), AVG() OVER(), COUNT() OVER() — calcular un total o promedio sin GROUP BY
- Navegación: LAG(), LEAD() — acceder a valores de fila anterior o siguiente (comparaciones mes a mes)
- Basado en marco: SUM() OVER (ORDER BY fecha ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) — ventanas deslizantes
🔨 La cláusula OVER() — cómo funcionan las funciones de ventana
- PARTITION BY — define los grupos (como GROUP BY, pero no colapsa filas)
- ORDER BY — define el orden de las filas dentro de cada partición
- ROWS/RANGE BETWEEN — define el marco (cuántas filas anteriores/siguientes incluir)
- Ejemplo: SUM(ingresos) OVER (PARTITION BY región ORDER BY fecha) calcula el total acumulado por región
- Si PARTITION BY se omite, la ventana abarca todo el conjunto de resultados
🔍 Análisis
Los 4 patrones más útiles con SQL real:
-- 1. Rank customers by revenue within each region
SELECT customer_name, region, revenue,
RANK() OVER (PARTITION BY region ORDER BY revenue DESC) AS region_rank
FROM customer_revenue;
-- 2. Running total by month
SELECT month, revenue,
SUM(revenue) OVER (ORDER BY month) AS running_total
FROM monthly_revenue;
-- 3. Month-over-month change (replaces self-join)
SELECT month, revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_month,
revenue - LAG(revenue) OVER (ORDER BY month) AS change
FROM monthly_revenue;
-- 4. 3-month moving average
SELECT month, revenue,
AVG(revenue) OVER (
ORDER BY month
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_avg_3m
FROM monthly_revenue;
✓️ Buena práctica
Cuándo usar funciones de ventana vs. otros enfoques:
- Totales acumulados y promedios móviles → siempre usar funciones de ventana, no subconsultas
- Rankings dentro de grupos → RANK() OVER (PARTITION BY) reemplaza subconsultas correlacionadas
- Mes a mes → LAG() reemplaza self-joins en la misma tabla
- Porcentaje del total → SUM(ingresos) OVER () en el denominador (sin PARTITION BY = suma de toda la tabla)
- Top N por categoría → ROW_NUMBER() en un CTE, luego filtrar WHERE row_num <= N
💡 Síntesis
Las funciones de ventana son el mayor aumento de productividad SQL para analistas de BI que ya conocen GROUP BY y JOINs. Eliminan los patrones de subconsultas más complicados y hacen que cálculos como totales acumulados, promedios móviles y rankings por grupo sean de una línea. La sintaxis de la cláusula OVER() parece extraña la primera vez — tiene todo el sentido después de la segunda.
👉 Si conoces GROUP BY y JOIN, estás al 90% del camino hacia las funciones de ventana.
La cláusula OVER() es el 10% que hace el otro 90% el doble de poderoso.