Funciones de ventana en SQL

Las funciones de ventana son una herramienta potente del lenguaje SQL que permite hacer cálculos complejos sobre grupos de filas relacionadas con la fila actual.

Cómo funcionan

Quizá te preguntes: «¿Qué significa eso de ventana?».

En una consulta SQL estándar, todos los conjuntos de filas se tratan como un único bloque continuo de datos sobre el que se calculan los valores agregados.

En cambio, cuando se usan funciones de ventana, para cada fila se determina un grupo de filas relacionadas con ella (o «ventana»), y sobre ese grupo se calcula un valor agregado propio.

Este conjunto de filas puede ser:

  • toda la tabla;
  • una partición de la tabla, es decir, un grupo de filas con los mismos valores en una o varias columnas;
  • o un marco de ventana, es decir, un rango concreto de filas dentro de la tabla o de la partición.

Por ejemplo, podemos definir el marco de ventana que se pasará a la función de ventana como la fila anterior + la fila actual de la tabla. Entonces, para cada fila, el valor de la función de agregación se calculará de forma distinta, porque los datos que llegan a la función cambian dinámicamente de una fila a otra. Es como si el marco «se deslizara» por la tabla.

Visualización

Las funciones de ventana siempre reciben como entrada una ventana de datos que indica el usuario y devuelven el resultado en una columna aparte.

Veamos cómo puede quedar. Para ello, tomamos la función de ventana AVG, que calcula la media, y esta pequeña tabla:

Tabla inicial

Y ahora veamos cómo funciona la función de ventana con distintas ventanas:

  • Si como ventana se indica toda la tabla, la ventana será la misma para todas las filas, a la función AVG le llegará siempre el mismo conjunto de datos y, por tanto, el resultado será el mismo.

    Esquema de la división en particiones

  • Si como ventana se indica una partición por la columna home_type, a la función AVG le llegará el conjunto de viviendas del mismo tipo y, por tanto, en la nueva columna se mostrará el precio medio de las viviendas cuyo tipo coincide con el de la fila actual de la tabla.

    Esquema de la división en particiones

  • Como marco de ventana también se puede indicar un conjunto de filas más específico. Por ejemplo, el marco se puede definir como «fila anterior + fila actual» de la tabla. Entonces quedará así:

    Esquema de la formación de los marcos de ventana

    Ten en cuenta que, para la primera fila, el marco solo contiene esa misma fila, porque no hay fila anterior.

Sintaxis de una función de ventana

MySQL 8.4
SELECT funcion_ventana(campo_tabla)
OVER (
    [PARTITION BY columna_particion [, ...]]
    [ORDER BY columna_orden [, ...]]
    [{ROWS | RANGE} marco_ventana]
)
  • [ ]parte opcional
  • { a | b }una de las opciones
  • ...se puede repetir
funcion_ventana

La función que se calcula sobre la ventana, por ejemplo AVG o SUM.

campo_tabla

La columna cuyos valores se pasan a la función, por ejemplo price en AVG(price).

columna_particion

Divide el resultado en particiones que no se solapan: en cada una entran las filas con los mismos valores en estas columnas.

Opcional
columna_orden

Define el orden de las filas dentro de la ventana. Es especialmente importante en las funciones de ventana de ranking.

Opcional
marco_ventana

Define el marco de la ventana: cuántas filas tomar antes y después de la fila actual.

Opcional

OVER define la ventana (el grupo de filas) que se pasará a la función de ventana. Si dejas la cláusula OVER () sin parámetros, la ventana estará formada por todas las filas del resultado de la consulta.

Dentro de OVER van 3 parámetros opcionales con los que se puede configurar la ventana de forma flexible. Nos detendremos en cada uno de ellos con más detalle en los próximos artículos.

Ejemplo de uso de una función de ventana

Vamos a intentar obtener, con funciones de ventana, la lista de nombres de los alumnos y cuántas personas hay en su clase.

Para empezar, obtengamos la lista de alumnos y el identificador de la clase en la que estudian:

MySQL 8.4
SELECT
    Student.first_name,
    Student.last_name,
    Student_in_class.class
FROM
    Student_in_class
JOIN
    Student ON Student_in_class.student = Student.id;
first_namelast_nameclass
NikolajSokolov9
VyacheslavEliseev9
IvanEfremov9
AnatolijZHdanov9
GeorgijNoskov9
ArtyomSergeev9
ArinaEvseeva9
AngelinaVoroncova9
EkaterinaUstinova9
RaisaLapina9
LeonidIgnatov9
SnezhanaSeliverstova9
SemyonBiryukov9
GeorgijBaranov8
YUliyaVishnyakova8
ValentinaBolshakova8
LeonidKryukov8
VladislavCvetkov8
SnezhanaMorozova8
LyubovBorisova8
AnfisaKalashnikova8
AnnaOsipova8
KristinaMyasnikova8
KristinaSmirnova8
BorisSimonov7
DmitrijTrofimov7
YAkovRozhkov7
FyodorDrozdov7
GlebStrelkov7
AngelinaLukina7
NinaOdincova7
ValeriyaNovikova7
GrigorijKapustin7
VitalijPanfilov7
SvyatoslavTarasov6
MatvejYAkushev6
IlyaAlekseev6
LyubovZaharova6
PolinaSidorova6
ElizavetaSamojlova6
YUliyaAvdeeva6
MatvejBogdanov6
IlyaFilippov6
DenisMel6
SvyatoslavMuravyov6
AnnaKulagina5
ZHannaFokina5
ValeriyaLapina5
ValentinaSazonova5
NataliyaMyasnikova5
ViktoriyaMakarova5
StanislavLazarev5
GennadijOvchinnikov5
RomanSHilov4
TimurSubbotin4
DanilaOsipov4
ArinaSilina4
NadezhdaZaharova4
LarisaSHCHerbakova4
AleksandraBelozyorova4
NatalyaDavydova4
MariyaFadeeva4
YUrijMarkov3
KirillSHubin3
GrigorijKolobov3
SemyonTrofimov3
VasilijUstinov3
ValentinaSHarova3
LarisaSavina3
GalinaOrekhova3
ArinaSHarapova2
ViktoriyaSergeeva2
VasilijKrasilnikov2
TimurRusakov2
GlebNesterov2
DenisMakarov2
ElizavetaSHilova2
VeraEvseeva1
MargaritaKabanova1
AngelinaLazareva1
SemyonVoronov1
InnokentijNekrasov1
ArtyomNikitin1
EgorBelyakov1

Y ahora, para calcular cuántos alumnos estudian en cada clase y mostrar esa información en una nueva columna, podemos usar una función de ventana:

MySQL 8.4
SELECT
    Student.first_name,
    Student.last_name,
    Student_in_class.class,
    COUNT(*) OVER (PARTITION BY Student_in_class.class) AS student_count_in_class
FROM
    Student_in_class
JOIN
    Student ON Student_in_class.student = Student.id;
first_namelast_nameclassstudent_count_in_class
EgorBelyakov17
ArtyomNikitin17
InnokentijNekrasov17
SemyonVoronov17
AngelinaLazareva17
MargaritaKabanova17
VeraEvseeva17
DenisMakarov27
ArinaSHarapova27
ViktoriyaSergeeva27
VasilijKrasilnikov27
TimurRusakov27
GlebNesterov27
ElizavetaSHilova27
KirillSHubin38
YUrijMarkov38
GrigorijKolobov38
SemyonTrofimov38
ValentinaSHarova38
LarisaSavina38
GalinaOrekhova38
VasilijUstinov38
TimurSubbotin49
RomanSHilov49
DanilaOsipov49
ArinaSilina49
NadezhdaZaharova49
LarisaSHCHerbakova49
AleksandraBelozyorova49
NatalyaDavydova49
MariyaFadeeva49
GennadijOvchinnikov58
StanislavLazarev58
ViktoriyaMakarova58
NataliyaMyasnikova58
ValentinaSazonova58
ValeriyaLapina58
ZHannaFokina58
AnnaKulagina58
IlyaFilippov611
SvyatoslavMuravyov611
DenisMel611
MatvejBogdanov611
YUliyaAvdeeva611
ElizavetaSamojlova611
PolinaSidorova611
LyubovZaharova611
IlyaAlekseev611
MatvejYAkushev611
SvyatoslavTarasov611
NinaOdincova710
BorisSimonov710
DmitrijTrofimov710
YAkovRozhkov710
FyodorDrozdov710
GlebStrelkov710
AngelinaLukina710
ValeriyaNovikova710
GrigorijKapustin710
VitalijPanfilov710
AnnaOsipova811
GeorgijBaranov811
YUliyaVishnyakova811
ValentinaBolshakova811
LeonidKryukov811
VladislavCvetkov811
LyubovBorisova811
AnfisaKalashnikova811
SnezhanaMorozova811
KristinaMyasnikova811
KristinaSmirnova811
VyacheslavEliseev913
IvanEfremov913
AnatolijZHdanov913
GeorgijNoskov913
ArtyomSergeev913
ArinaEvseeva913
AngelinaVoroncova913
EkaterinaUstinova913
RaisaLapina913
LeonidIgnatov913
SnezhanaSeliverstova913
SemyonBiryukov913
NikolajSokolov913

Qué hace nuestra función de ventana

La expresión PARTITION BY Student_in_class.class divide todas las filas de la tabla en particiones según la columna class. Así, para cada fila, a la función de ventana solo le llegarán las filas de la tabla en las que el valor de class coincide con el de la fila actual.

La función COUNT, por su parte, devuelve el número de filas que recibe, y así obtenemos cuántos alumnos estudian en cada clase.

Orden de ejecución de las funciones de ventana en SELECT

Al usar funciones de ventana es importante entender en qué orden se ejecutan. Como puedes ver en el esquema de abajo, las funciones de ventana se ejecutan en el penúltimo paso, después del filtrado y la agrupación, pero antes de la ordenación final de los resultados.

Orden de ejecución de la función de ventana en una consulta SELECT

Conclusión

En este artículo hemos visto brevemente el concepto de funciones de ventana, sus posibilidades y su utilidad práctica. En los próximos artículos veremos con más detalle cada aspecto de las funciones de ventana.

Y, para terminar, comprobemos si lo hemos entendido todo:

¿Cuál es la diferencia clave entre las funciones de ventana y las funciones de agregación con agrupación en SQL?