🎯 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.

👉 Las funciones de ventana realizan cálculos en un conjunto de filas relacionadas con la fila actual — sin colapsar esas filas en una. Esa es la distinción clave con GROUP BY. GROUP BY devuelve una fila por grupo. Las funciones de ventana devuelven una fila por fila original, con una columna calculada adicional.

⚠️ 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;
Estos 4 patrones cubren la mayoría de los casos de uso de funciones de ventana en BI. El patrón de ranking solo reemplaza el patrón de subconsulta 'top N por grupo' más común que ralentiza muchos reportes en producción.

✓️ 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.