Si alguna vez intentaste calcular un total acumulado, clasificar productos por ventas o suavizar datos con ruido mediante un promedio móvil, probablemente recurriste a Excel o a Python, pero las funciones de ventana de SQL pueden hacer todo esto directamente en la base de datos, con frecuencia más rápido y con menos código. Para analistas de datos principiantes, usuarios de Excel y aspirantes a programadores SQL, las funciones de ventana de SQL son una de las habilidades de mayor impacto que puedes aprender después de dominar lo básico de SELECT, WHERE y GROUP BY.

Una función de ventana realiza un cálculo sobre un conjunto de filas (una “ventana”) relacionadas con la fila actual, sin colapsar esas filas en un único resultado como lo hace GROUP BY. Eso significa que puedes calcular un total acumulado, un ranking o un promedio móvil y aun así ver cada fila de detalle.

En esta guía aprenderás cómo funcionan las funciones de ventana de SQL, cómo calcular paso a paso totales acumulados, rankings y promedios móviles, y cómo evitar los errores más comunes de los principiantes, con ejemplos claros y prácticos que puedes adaptar a tus propios datos.

¿Qué Es una Función de Ventana?

Una función de ventana usa la cláusula OVER() para definir una “ventana” de filas que el cálculo debe considerar. A diferencia de GROUP BY, que reduce varias filas a una sola fila de resumen, una función de ventana conserva cada fila original y agrega a su lado una columna calculada.

La sintaxis básica se ve así:

SELECT
    column1,
    column2,
    SOME_FUNCTION() OVER (
        PARTITION BY grouping_column
        ORDER BY sorting_column
    ) AS result
FROM table_name;
  • PARTITION BY: es opcional; divide las filas en grupos, de forma similar a GROUP BY, pero sin colapsarlas.
  • ORDER BY: controla el orden en que se procesan las filas dentro de cada partición, lo cual importa para los totales acumulados y los rankings.

A lo largo de esta guía usaremos una tabla Sales con las columnas: SaleDate, Region, Amount.

Calcular Totales Acumulados

Un total acumulado (o suma acumulativa) va sumando los valores a medida que avanzas por las filas en orden. Usa SUM() como función de ventana con ORDER BY:

SELECT
    SaleDate,
    Amount,
    SUM(Amount) OVER (ORDER BY SaleDate) AS RunningTotal
FROM Sales;

El RunningTotal de cada fila incluye su propio Amount más el Amount de cada fila anterior, según el ORDER BY SaleDate.

Totales Acumulados Dentro de Grupos

Agrega PARTITION BY para calcular un total acumulado separado para cada grupo; por ejemplo, un total acumulado por región en lugar de uno solo para toda la tabla:

SELECT
    Region,
    SaleDate,
    Amount,
    SUM(Amount) OVER (
        PARTITION BY Region
        ORDER BY SaleDate
    ) AS RunningTotalByRegion
FROM Sales;

Ahora el total acumulado se reinicia para cada región, en lugar de acumularse entre todas juntas.

Clasificar Filas con las Funciones de Ventana de SQL

SQL ofrece tres funciones de clasificación muy relacionadas:

  1. ROW_NUMBER(): asigna un número único y secuencial a cada fila, aunque los valores empaten.
  2. RANK(): asigna el mismo rango a los valores empatados y luego salta el siguiente número de rango (1, 2, 2, 4).
  3. DENSE_RANK(): asigna el mismo rango a los valores empatados, sin saltar el siguiente número (1, 2, 2, 3).
SELECT
    Region,
    Amount,
    ROW_NUMBER() OVER (ORDER BY Amount DESC) AS RowNum,
    RANK() OVER (ORDER BY Amount DESC) AS Rank,
    DENSE_RANK() OVER (ORDER BY Amount DESC) AS DenseRank
FROM Sales;

Clasificar Dentro de Grupos

Igual que con los totales acumulados, la clasificación resulta mucho más útil cuando se combina con PARTITION BY; por ejemplo, clasificando las ventas dentro de cada región por separado:

SELECT
    Region,
    Amount,
    RANK() OVER (
        PARTITION BY Region
        ORDER BY Amount DESC
    ) AS RegionRank
FROM Sales;

Esta es una técnica común para preguntas como “¿cuál fue el día de mayores ventas de cada región?”; solo tendrías que filtrar el resultado con WHERE RegionRank = 1.

Calcular Promedios Móviles

Un promedio móvil suaviza las fluctuaciones de corto plazo al promediar un número fijo de filas cercanas. Las funciones de ventana de SQL lo manejan con una cláusula de marco (frame clause), usando ROWS BETWEEN:

SELECT
    SaleDate,
    Amount,
    AVG(Amount) OVER (
        ORDER BY SaleDate
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS SevenDayMovingAverage
FROM Sales;

Esto calcula el promedio de la fila actual más las 6 filas anteriores, un promedio móvil simple de 7 días cuando tus datos tienen una fila por día.

Patrones Comunes de la Cláusula de Marco

  • ROWS BETWEEN 6 PRECEDING AND CURRENT ROW: un promedio con ventana hacia atrás (por ejemplo, los últimos 7 días, incluido hoy).
  • ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: equivale a un total acumulado o a un promedio acumulado desde la primera fila.
  • ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING: un promedio centrado, útil para suavizar tendencias de forma simétrica.

Errores Comunes que Debes Evitar

  • Olvidar el ORDER BY dentro de OVER() en totales acumulados o rankings: sin él, el orden del cálculo es impredecible.
  • Confundir RANK() y DENSE_RANK(), sobre todo cuando los empates importan para el filtrado posterior.
  • Usar PARTITION BY cuando querías GROUP BY, o al revés; recuerda que las funciones de ventana conservan todas las filas, mientras que GROUP BY las colapsa.
  • Filtrar directamente sobre una función de ventana en WHERE. Las funciones de ventana no se pueden usar en WHERE; envuelve la consulta en una subconsulta o CTE y filtra en la consulta externa.
-- This will NOT work:
SELECT * FROM Sales WHERE RANK() OVER (ORDER BY Amount DESC) = 1;

-- Do this instead:
WITH RankedSales AS (
    SELECT *, RANK() OVER (ORDER BY Amount DESC) AS Rank
    FROM Sales
)
SELECT * FROM RankedSales WHERE Rank = 1;

Conclusión

Las funciones de ventana de SQL te dan el poder de calcular totales acumulados, rankings y promedios móviles sin salir nunca de la base de datos: no necesitas exportar datos a Excel ni escribir scripts de Python aparte. La cláusula OVER(), combinada con PARTITION BY y ORDER BY, te permite conservar cada fila de detalle y al mismo tiempo agregar cálculos potentes fila por fila.

En resumen: usa SUM() OVER (ORDER BY ...) para totales acumulados, ROW_NUMBER(), RANK() o DENSE_RANK() para rankings, y AVG() OVER (... ROWS BETWEEN ...) para promedios móviles, y recurre a PARTITION BY siempre que necesites que estos cálculos se reinicien por grupo en lugar de correr sobre toda tu tabla.

Cuando las funciones de ventana de SQL te hagan clic, se convierten en una de las herramientas más prácticas de tu caja de herramientas de SQL, transformando flujos de trabajo de varios pasos en hojas de cálculo en una sola consulta legible que puedes ejecutar directamente sobre tus datos en vivo.