¿Cómo saco un promedio móvil de 3 meses en SQL?
Suaviza las ventas de Centro con un promedio de los últimos 3 meses, escribiendo tú el marco de la ventana.
Curso gratis. Tu Maestro, una IA, .
Las ventas mensuales suben y bajan. Un promedio móvil suaviza esos brincos: en cada mes, promedias ese mes y los dos anteriores. Así se ve mejor la tendencia.
Es una ventana con un marco que escribes tú:
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW: las dos filas anteriores y la actual, tres en total.
Como en la página anterior, ROWS cuenta filas. Y como Centro tiene un registro por mes, tres filas son tres meses.
AVG(ventas) OVER (
ORDER BY mes
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
)Inténtalo
Calcula el promedio móvil de 3 meses de Centro, redondeado sin decimales con ROUND(...). Antes, contesta la pregunta de abajo. Si tu promedio sale igual al acumulado, pregúntale a tu Maestro.
Comprueba lo que sabes: Antes de correr: ¿qué va a salir en promedio_3_meses en enero, cuando todavía no hay dos meses anteriores?
Para la sucursal Centro, regresa mes, ventas y promedio3meses: el promedio de ese mes y los dos anteriores, con ROUND(...) sin decimales. Ordena por mes. Terminaste cuando ves 12 filas y marzo dice 166500.
Tu turno
Para la sucursal Centro, regresa mes, ventas y promedio_3_meses: el promedio de ese mes y los dos anteriores, con ROUND(...) sin decimales. Ordena por mes. Terminaste cuando ves 12 filas y marzo dice 166500.
Tablas: ventas_mensuales (sucursal, mes, ventas)
ventas_mensuales · 35 filas
| Columna | Tipo |
|---|---|
| sucursal Nombre de la sucursal: Centro, Norte o Sur. | VARCHAR |
| mes Primer día del mes que resume la fila. | DATE |
| ventas Ventas del mes, en pesos. | BIGINT |
Primeras filas
| sucursal | mes | ventas |
|---|---|---|
| Centro | 2025-01-01 | 165100 |
| Centro | 2025-02-01 | 155700 |
| Centro | 2025-03-01 | 178700 |
Cada ejecución empieza con copias nuevas de las tablas del curso, así que puedes probar lo que quieras. Tab agrega sangría; para salir del editor, presiona Escape y luego Tab.
Listo para ejecutar tu consulta.
Fila por fila
El marco ahora se desliza: no empieza siempre en enero, empieza dos filas antes de la actual. Así se movió en los primeros meses de Centro al correr la solución en el sandbox:
| mes | ventas | filas en el marco | promedio_3_meses |
|---|---|---|---|
| 2025-01-01 | 165100 | enero | 165100 |
| 2025-02-01 | 155700 | enero y febrero | 160400 |
| 2025-03-01 | 178700 | enero, febrero y marzo | 166500 |
| 2025-04-01 | 175800 | febrero, marzo y abril | 170067 |
En abril, enero ya salió del marco. Esa es la diferencia con la suma acumulada de hace dos páginas: allá el inicio del marco estaba fijo en UNBOUNDED PRECEDING y aquí se mueve con cada fila.
En la gráfica, la línea de las ventas brinca y la del promedio de 3 meses la sigue más tranquila. La segunda sale de la ventana mean de Vega-Lite con un marco de dos filas antes a la actual.
Fíjate otra vez en enero y febrero: el marco pide "hasta dos filas antes", y al principio no las hay. No sale NULL; sale el promedio de las filas que sí están.
Lo que se rompe en la vida real
Promedios incompletos. Si en tu reporte solo quieres promedios de tres meses completos, cuenta las filas del marco con COUNT(*) sobre la misma ventana y quédate con las que tienen 3. En el sandbox, esta consulta regresó 10 filas, de marzo a diciembre:
SELECT mes, ventas,
ROUND(AVG(ventas) OVER w) AS promedio_3_meses
FROM ventas_mensuales
WHERE sucursal = 'Centro'
WINDOW w AS (ORDER BY mes ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
QUALIFY COUNT(*) OVER w = 3
ORDER BY mes;Un mes que falta. En Sur no hay junio. Con ROWS, el marco de julio son las tres filas anteriores que sí existen: abril, mayo y julio. Si lo que quieres son los tres meses del calendario, RANGE acepta una distancia en el valor del ORDER BY: RANGE BETWEEN INTERVAL 2 MONTH PRECEDING AND CURRENT ROW toma las filas cuyo mes está a dos meses o menos. En el sandbox, para Sur salió así:
| mes | ventas | promedio con ROWS | promedio con RANGE | filas en RANGE |
|---|---|---|---|---|
| 2025-05-01 | 119600 | 117767 | 117767 | 3 |
| 2025-07-01 | 117600 | 119400 | 118600 | 2 |
| 2025-08-01 | 122900 | 120033 | 120250 | 2 |
| 2025-09-01 | 114500 | 118333 | 118333 | 3 |
En julio y agosto los dos promedios se separan: RANGE no encuentra junio y promedia dos meses, ROWS se va hasta abril para juntar tres filas. Ninguno está mal; responden preguntas distintas.
Compruébalo tú
Antes, contesta la pregunta de abajo. Si tu RANGE da error o no cambia nada, pregúntale a tu Maestro.
Comprueba lo que sabes: Con RANGE BETWEEN INTERVAL 2 MONTH PRECEDING AND CURRENT ROW, ¿cuántas filas entran en el marco de julio de Sur?
Para la sucursal Sur, regresa mes, ventas, promrows (el promedio con ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), promrange (con RANGE BETWEEN INTERVAL 2 MONTH PRECEDING AND CURRENT ROW) y mesesenrange (COUNT() con el mismo marco de RANGE). Redondea los promedios sin decimales con
Tu turno
Para la sucursal Sur, regresa mes, ventas, prom_rows (el promedio con ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), prom_range (con RANGE BETWEEN INTERVAL 2 MONTH PRECEDING AND CURRENT ROW) y meses_en_range (COUNT(*) con el mismo marco de RANGE). Redondea los promedios sin decimales con ROUND(...) y ordena por mes. Terminaste cuando julio dice 119400, 118600 y 2.
Tablas: ventas_mensuales (sucursal, mes, ventas)
ventas_mensuales · 35 filas
| Columna | Tipo |
|---|---|
| sucursal Nombre de la sucursal: Centro, Norte o Sur. | VARCHAR |
| mes Primer día del mes que resume la fila. | DATE |
| ventas Ventas del mes, en pesos. | BIGINT |
Primeras filas
| sucursal | mes | ventas |
|---|---|---|
| Centro | 2025-01-01 | 165100 |
| Centro | 2025-02-01 | 155700 |
| Centro | 2025-03-01 | 178700 |
Cada ejecución empieza con copias nuevas de las tablas del curso, así que puedes probar lo que quieras. Tab agrega sangría; para salir del editor, presiona Escape y luego Tab.
Listo para ejecutar tu consulta.
Un promedio centrado. Si el promedio es para mirar la forma de la serie y no para reportar el último dato, puedes poner el mes en medio: ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING. En el sandbox, marzo de Centro dio 170067, el mismo número que el promedio de 3 meses de abril, porque son las mismas tres filas.
La pregunta típica de entrevista es "calcula el promedio móvil de 7 días de los usuarios activos". La trampa es la misma que con Sur: ROWS BETWEEN 6 PRECEDING AND CURRENT ROW cuenta 7 filas, y solo son 7 días si hay una fila por día, sin huecos. Di en voz alta qué supones, y ofrece la versión con RANGE e INTERVAL o rellenar el calendario como en el módulo 3. Si quieres practicar esta pregunta, pídele a tu Maestro que te la haga.
Antes de poner un promedio móvil en un tablero, revisa dos cosas: si los primeros periodos deben mostrarse (o filtrarse con COUNT(*)), y si tu serie tiene huecos. Escribe el marco completo en la consulta, aunque sea el de siempre, para que quien la lea sepa qué decidiste.
ROUND(AVG(ventas) OVER (
ORDER BY mes
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
)) AS promedio_centradoComprueba lo que sabes: Una función más que depende del marco por omisión: en Centro, ¿qué da LAST_VALUE(ventas) OVER (ORDER BY mes) en la fila de marzo?
Error común: LAST_VALUE y el marco por omisión
Con ORDER BY y sin marco escrito, el marco termina en la fila actual. Por eso LAST_VALUE(ventas) OVER (ORDER BY mes) no da diciembre: en cada fila da las ventas de esa misma fila. En una corrida con los datos de Centro, marzo dio 178700: sus propias ventas. Para el último valor de toda la partición, escribe el marco completo: ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.