¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Funciones de ventana en SQL

Después de esta lección: Puedo añadir un total acumulado o una posición junto a cada fila sin perder ninguna fila.

Lección de 55 minSin comandos

Qué es una función de ventana

Una función de ventana calcula un valor para cada fila mirando otras filas, y no elimina ninguna. Con ella añades un total acumulado o una posición junto a cada fila de la tabla.

La papelería quiere ver, día a día, cuánto lleva vendido en el mes. Un GROUP BY daría un solo total. Aquí hace falta el total hasta cada día.

Al terminar esta lección

Nota

Puedo añadir un total acumulado o una posición junto a cada fila sin perder ninguna fila.

Palabras clave

PalabraQué significaEjemplo
Función de ventanacalcula un valor por fila mirando otras filas
OVERla palabra que abre la ventana
Total acumuladola suma de todo hasta la fila actual
Posiciónel lugar de una fila al ordenarRANK()
Empatedos filas con el mismo valor al ordenar

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

OVER añade un cálculo sin quitar filas

GROUP BY junta filas: devuelve una por grupo. Una función de ventana no junta nada. Cada fila sigue en el resultado y recibe un valor más.

La palabra OVER abre la ventana. Sin nada dentro, la ventana es toda la tabla. Con ORDER BY dentro, la ventana crece fila a fila.

Ejemplo resuelto: el total acumulado

dayamountrunning_total
2026-03-02120120
2026-03-0395215
2026-03-04140355
2026-03-05120475
2026-03-06160635
2026-03-0790725
2026-03-08150875
2026-03-091401015

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Pruébalo: un total acumulado

Código: duckdb
-- Cada día con lo que llevamos vendido hasta ese día.
select day, amount,
  sum(amount) over (order by day) as running_total
from daily_takings
order by day;
-- Cambia sum(amount) por count(*) y pulsa Run.

RANK pone cada fila en su lugar

RANK() OVER (ORDER BY amount DESC) da el 1 al día con más ventas. Si dos días empatan, RANK les da la misma posición y se salta la siguiente.

DENSE_RANK no se salta ninguna. ROW_NUMBER nunca repite; añade un segundo criterio en ORDER BY para que el desempate sea siempre el mismo.

Ejemplo resuelto: tres formas de numerar

dayamountRANKDENSE_RANKROW_NUMBER
2026-03-06160111
2026-03-08150222
2026-03-04140333
2026-03-09140334
2026-03-02120545
2026-03-05120546
2026-03-0395757
2026-03-0790868

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Pruébalo: las posiciones

Código: duckdb
-- La posición de cada día, del mejor al peor.
select day, amount,
  rank() over (order by amount desc) as position
from daily_takings
order by position, day;
-- Cambia desc por asc y pulsa Run.

LAG mira la fila anterior

LAG(amount) trae el valor de la fila anterior, según el orden de la ventana. Restarlo al valor actual da el cambio respecto al día de antes.

La primera fila no tiene anterior. Allí LAG devuelve NULL, que significa «sin dato». LEAD hace lo mismo hacia delante.

Ejemplo resuelto: el cambio de cada día

dayamountchange
2026-03-02120NULL
2026-03-0395-25
2026-03-0414045
2026-03-05120-20
2026-03-0616040
2026-03-0790-70
2026-03-0815060
2026-03-09140-10

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Pruébalo: la diferencia con ayer

Código: duckdb
-- La diferencia con el día anterior.
select day, amount,
  amount - lag(amount) over (order by day) as change
from daily_takings
order by day;
-- Cambia lag por lead y mira qué fila queda vacía.

Un error frecuente

Advertencia

Mucha gente escribe una función de ventana dentro de WHERE. DuckDB responde con un Binder Error: WHERE clause cannot contain window functions.WHERE se ejecuta antes que las ventanas. Calcula la ventana en una subconsulta y filtra fuera.

Pruébalo: filtrar por el total acumulado

Código: duckdb
-- Primero la ventana, dentro; después el filtro, fuera.
select day, running_total
from (
  select day, sum(amount) over (order by day) as running_total
  from daily_takings
)
where running_total > 500
order by day;
-- Cambia 500 por 800 y pulsa Run.

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.