Las funciones de ventana (window functions) en SQL permiten hacer cálculos sobre un conjunto de filas relacionadas sin colapsar el resultado, a diferencia de GROUP BY. La cláusula OVER define esa «ventana» de filas, y PARTITION BY la divide en grupos — como un GROUP BY que no oculta el detalle de cada fila.
Datos de ejemplo
Partimos de esta tabla base para todos los ejemplos:
| depto | cod | nombre |
|---|---|---|
| 1 | 100 | Fran |
| 1 | 101 | Chris |
| 2 | 102 | Alberto |
| 2 | 103 | Hernan |
| 3 | 104 | Julia |
ROW_NUMBER con PARTITION BY
Este ejemplo numera las filas dentro de cada departamento, ordenadas por código. El contador se reinicia en 1 cada vez que cambia el valor de depto:
SELECT depto, cod, nombre,
ROW_NUMBER() OVER (PARTITION BY depto ORDER BY cod) AS rw
FROM departamentosResultado
| depto | cod | nombre | rw |
|---|---|---|---|
| 1 | 100 | Fran | 1 |
| 1 | 101 | Chris | 2 |
| 2 | 102 | Alberto | 1 |
| 2 | 103 | Hernan | 2 |
| 3 | 104 | Julia | 1 |
RANK y DENSE_RANK: variantes de numeración
ROW_NUMBER siempre da números consecutivos únicos, incluso si hay empates. Si necesitas que los empates compartan posición, existen dos alternativas:
RANK()— da la misma posición a los empates, pero deja «huecos» en la numeración siguiente (1, 1, 3).DENSE_RANK()— da la misma posición a los empates, sin dejar huecos (1, 1, 2).
SELECT depto, cod, nombre,
RANK() OVER (PARTITION BY depto ORDER BY cod) AS posicion_rank,
DENSE_RANK() OVER (PARTITION BY depto ORDER BY cod) AS posicion_dense
FROM departamentosSuma de ventas por día
Calcula el total acumulado de ventas partiendo los datos por fecha. Al usar ORDER BY dentro de OVER, la suma se vuelve un running total: cada fila acumula el valor de las anteriores dentro de su misma partición:
SELECT fecha, venta_dia,
SUM(venta_dia) OVER (PARTITION BY fecha ORDER BY fecha DESC) AS suma_acumulada
FROM ventasSueldos máximos por departamento
Obtiene el sueldo más alto dentro de cada departamento, sin agrupar ni perder las filas individuales — cada empleado sigue apareciendo en su propia fila, junto al máximo de su equipo:
SELECT depto, nombre, sueldo,
MAX(sueldo) OVER (PARTITION BY depto) AS sueldo_max_depto
FROM empleadosLAG y LEAD: comparar con la fila anterior o siguiente
Otro uso común de OVER es comparar el valor de una fila con el de la fila anterior o siguiente dentro de la misma partición — útil para calcular variaciones día a día o mes a mes:
SELECT depto, cod, nombre,
LAG(cod) OVER (PARTITION BY depto ORDER BY cod) AS cod_anterior,
LEAD(cod) OVER (PARTITION BY depto ORDER BY cod) AS cod_siguiente
FROM departamentosPor qué usar OVER en vez de GROUP BY
La diferencia clave es que GROUP BY reduce el número de filas del resultado (una por grupo), mientras que OVER/PARTITION BY mantiene todas las filas originales y solo agrega una columna calculada junto a cada una. Esto es especialmente útil cuando necesitas comparar el valor de una fila contra un agregado de su grupo (como el sueldo máximo del departamento) sin perder el detalle individual.