Qué hacen PARTITION BY y los marcos
PARTITION BY reparte las filas en grupos y calcula la función de ventana dentro de cada grupo, sin juntar las filas. Un marco limita cuántas filas vecinas entran en el cálculo, por ejemplo para una media móvil.
Sirve para dos preguntas habituales: quién es el mejor de cada grupo y cuánto se alquiló de media los últimos tres días.
Al terminar esta lección
Nota
Puedo ordenar dentro de cada grupo y calcular una media sobre una ventana móvil.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Partición | un grupo de filas dentro de la ventana | |
| PARTITION BY | reparte las filas en particiones | PARTITION BY class_group |
| Marco | las filas vecinas que entran en el cálculo | ROWS BETWEEN 2 PRECEDING AND CURRENT ROW |
| Media móvil | la media de la fila actual y las anteriores | |
| Subconsulta | una consulta escrita dentro de otra |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
PARTITION BY reparte las filas en grupos
PARTITION BY class_group hace que la función empiece de nuevo en cada grupo. El primer puesto de la clase A y el de la clase B son dos puestos distintos.
A diferencia de GROUP BY, todas las filas siguen ahí. Sirve con RANK, con ROW_NUMBER y con agregados como AVG.
Ejemplo resuelto: el puesto dentro de cada grupo
| student | class_group | mark | place |
|---|---|---|---|
| Mateo | A | 85 | 1 |
| Sofía | A | 85 | 1 |
| Lucía | A | 72 | 3 |
| Hugo | A | 60 | 4 |
| Valeria | B | 91 | 1 |
| Daniel | B | 78 | 2 |
| Adrián | B | 78 | 2 |
| Paula | B | 66 | 4 |
| Carmen | C | 88 | 1 |
| Irene | C | 70 | 2 |
| Pablo | C | 54 | 3 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Pruébalo: la media de cada grupo
-- La media de cada grupo, junto a cada alumno.
select student, class_group, mark,
round(avg(mark) over (partition by class_group), 1) as group_avg
from marks
order by student_id;
-- Cambia avg por max y pulsa Run.Quedarse con los mejores de cada grupo
Para quedarte con los dos mejores de cada grupo, numera con ROW_NUMBER y PARTITION BY. Después filtra n <= 2 en la consulta de fuera.
Añade un segundo criterio al ORDER BY, como student_id. Así el desempate es siempre el mismo.
DuckDB tiene también QUALIFY, que filtra por una ventana sin escribir la subconsulta.
Pruébalo: los dos mejores con una subconsulta
-- Los dos mejores de cada grupo, con una subconsulta.
select student, class_group, mark
from (
select *,
row_number() over (partition by class_group order by mark desc, student_id) as n
from marks
)
where n <= 2
order by class_group, n;
-- Cambia 2 por 1 y pulsa Run.Pruébalo: los mismos con QUALIFY
-- Lo mismo con QUALIFY, sin subconsulta.
select student, class_group, mark
from marks
qualify row_number() over (partition by class_group order by mark desc, student_id) <= 2
order by class_group, mark desc, student_id;
-- Cambia desc por asc para ver los dos peores.Un marco elige las filas vecinas
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW toma la fila actual y las dos anteriores. Con ese marco, AVG da una media móvil de tres días.
Al principio no hay tres filas todavía. El marco usa las que existen, y por eso el primer valor es la media de una sola fila.
Ejemplo resuelto: una media móvil de tres días
| day | rides | Filas en el marco | moving_avg |
|---|---|---|---|
| 2026-04-06 | 40 | 1 | 40.0 |
| 2026-04-07 | 46 | 2 | 43.0 |
| 2026-04-08 | 52 | 3 | 46.0 |
| 2026-04-09 | 31 | 3 | 43.0 |
| 2026-04-10 | 58 | 3 | 47.0 |
| 2026-04-11 | 70 | 3 | 53.0 |
| 2026-04-12 | 64 | 3 | 64.0 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Pruébalo: la media móvil
-- La media de cada día y los dos anteriores.
select day, rides,
round(avg(rides) over (order by day rows between 2 preceding and current row), 1) as moving_avg
from daily_rides
order by day;
-- Cambia 2 preceding por 1 preceding y pulsa Run.Un error frecuente
Advertencia
Mucha gente cree que SUM(x) OVER (ORDER BY x) suma fila a fila. Con empates, el marco por defecto trata las filas iguales como un bloque y les da el mismo total. Escribe ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW para ir de una en una.
Ejemplo resuelto: dos totales con empates
| student_id | mark | Marco por defecto | Con ROWS |
|---|---|---|---|
| 11 | 70 | 250 | 250 |
| 6 | 78 | 478 | 400 |
| 8 | 78 | 478 | 478 |
| 2 | 85 | 648 | 563 |
| 3 | 85 | 648 | 648 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.