La idea
Las preguntas de negocio suenan distintas ("¿estamos creciendo?", "¿qué se vende mejor?", "¿los clientes nuevos vuelven?"), pero las consultas detrás comparten una forma. Primero decides el nivel de detalle: lo que representa una fila de tu respuesta, como un mes, una categoría o un trimestre de registro. Luego agregas a ese nivel. Después usas una ventana para poner cada fila junto a la fila con la que la comparas. Al final decides entre qué divides.
La mayoría de los números equivocados en una revisión de negocio vienen de un error en una de esas tres decisiones, no de un error de sintaxis SQL. La consulta se ejecuta, el gráfico se ve plausible y el número sigue estando mal.
Figura 1
La forma detrás de casi toda consulta de revisión de negocio
-
ordersuna fila por pedido -
monthly AS (… GROUP BY month)una fila por mes -
LAG(orders, 1) / LAG(orders, 12)mirar atrás, mismas filas -
SELECT … / growthuna fila por mes, con una comparación
- Parte de las filas crudas. 12,000 pedidos son demasiado detalle para "¿estamos creciendo?", y todavía no tienen la forma que necesita una ventana.
- Agrega primero, en un CTE, al nivel de detalle que vas a reportar: una fila por mes. Ahora cada fila es una unidad comparable.
- Solo ahora agregas una ventana.
LAG(orders, 1)lee la fila anterior (el mes pasado);LAG(orders, 12)lee doce filas atrás (el mismo mes del año pasado). - El
SELECTfinal divide este mes entre el valor buscado. Toda decisión que puede salir mal vive en una de las tres primeras cajas.
Por qué existe
Contar pedidos es fácil. Decir que los ingresos "subieron" es difícil, porque necesita una base de comparación, y esa base es una elección. Subieron frente a qué: el mes pasado, el mismo mes del año pasado, el plan, el promedio? Cualquiera que elijas debe aplicarse al mismo tipo de periodo, y definirse igual cada vez, o dos personas reportarán números distintos para el mismo trimestre.
Por eso este capítulo practica patrones y no funciones. Las funciones ya las conoces. Lo importante es el hábito de construir cada métrica en el mismo orden y revisar cada paso, para que una revisión de 20 métricas no necesite 20 ideas nuevas.
Cómo funciona por dentro
Paso 1: agrega al nivel de detalle y luego compara
Las funciones de ventana trabajan sobre filas. Para comparar meses primero necesitas una fila por
mes, así que los totales mensuales van en un CTE y las ventanas leen de él. Si aplicas LAG a los
pedidos crudos, comparas un pedido con el pedido anterior, que no es un mes.
El nivel de detalle también debe estar completo. LAG(orders, 12) significa doce filas atrás, que
es el mismo mes del año pasado solo si no falta ningún mes entre medio. Une los totales mensuales a un
calendario para que los meses sin pedidos aparezcan con cero.
Paso 2: elige la comparación
Mes contra mes y año contra año usan la misma ventana con un desfase distinto.
Figura 2
El mismo mes, dos tasas de crecimiento
| month | orders | prev_month | prev_year | mom | yoy |
|---|---|---|---|---|---|
| 2025-01 | 296 | 698 | 235 | -57.6% | +26.0% |
| 2025-02 | 235 | 296 | 189 | -20.6% | +24.3% |
| 2025-03 | 326 | 235 | 300 | +38.7% | +8.7% |
- Enero de 2025 tuvo 296 pedidos. Comparado con el mes anterior, diciembre de 2024 (698), es una caída de 57.6%.
- Comparado con enero de 2024 (235), el mismo mes un año antes, es un crecimiento de 26.0%. Diciembre es el pico de fiestas, así que "mes contra mes" mide sobre todo la temporada.
- Las dos son correctas. Responden preguntas distintas: MoM te dice qué acaba de cambiar, YoY te dice si el negocio crece una vez que la temporada se cancela.
El crecimiento es (este - anterior) / anterior, y el primer periodo de cada comparación no tiene
fila anterior, así que sale NULL. Divide con NULLIF(previous, 0) para que una base en cero dé
NULL en lugar de un error.
Dos reglas más. Compara periodos completos con periodos completos: un mes que va por la mitad se ve como un derrumbe junto a uno completo, que es la trampa del mes parcial. Y compara la misma definición: si "pedidos" excluye los cancelados este mes, tiene que excluirlos también el año pasado.
Paso 3: reinicia cuando la pregunta se reinicia
Un total acumulado responde "¿cómo vamos?" solo si empieza de nuevo cuando empieza el periodo.
Figura 3
Un total del año a la fecha tiene que reiniciarse cada enero
| month | orders | ytd_reset | ytd_no_partition |
|---|---|---|---|
| 2024-11 | 629 | 3348 | 3348 |
| 2024-12 | 698 | 4046 | 4046 |
| 2025-01 | 296 | 296 | 4342 |
| 2025-02 | 235 | 531 | 4577 |
- Hasta diciembre de 2024 el total acumulado llega a 4,046 pedidos, el año completo.
- Sin un
PARTITION BYpor año, la ventana nunca se detiene. Enero de 2025 muestra 4,342: los pedidos del año pasado más el primer mes de este. SUM(orders) OVER (PARTITION BY year ORDER BY month)empieza una partición nueva en enero. Ahora enero muestra 296 y febrero 531, que es lo que significa "año a la fecha".
Pon el periodo en PARTITION BY (el año) y la secuencia en ORDER BY (el mes). La misma idea da
"clientes en lo que va del trimestre" o "gasto desde el registro".
Paso 4: top N con empates, embudos y cohortes
Estos patrones siguen la misma receta. El top N por grupo es una ventana de ranking dentro de un CTE, y
tu elección entre ROW_NUMBER, RANK o DENSE_RANK es una declaración sobre los empates. Un embudo
cuenta cuántas sesiones llegan a cada paso y luego divide cada paso entre el anterior: las sesiones de
escritorio convierten 563 de 14,541, un 3.9%, y las de móvil 520 de 21,549, un 2.4%, así que el
problema del móvil solo se ve cuando divides. Una cuadrícula de cohortes agrupa a los clientes por su
primer pedido y cuenta quiénes volvieron a pedir en cada periodo posterior.
Paso 5: di cuál es el denominador
Todo porcentaje esconde un denominador. La "tasa de activación" es clientes activados divididos entre cuáles clientes: todos los que se registraron, o solo aquellos cuyo trimestre ya terminó? En una cuadrícula de retención, una cohorte que se registró el mes pasado no pudo haber tenido doce meses para volver, así que debe quedar fuera de la columna de doce meses en lugar de contarse como cero. Escribe el denominador en una frase antes de escribir el SQL.
Paso 6: revisa una fila a mano
Antes de entregar un patrón, elige una fila del resultado y reconstrúyela con una consulta aparte y sencilla. Cuenta directamente los pedidos de enero de 2025 y compáralos con el CTE. Busca enero de 2024 y divide a mano. Si el número a mano y el del patrón coinciden en una fila donde algo pudo salir mal (el primer mes de un año, un mes con empates, la cohorte más reciente), lo demás probablemente está bien. Si no coinciden, el nivel de detalle, la ventana o el denominador está mal, y ya sabes qué fila depurar.
Errores comunes
"Crecimiento es crecimiento: puedo comparar cualquier par de periodos"
Una comparación mide la diferencia entre dos periodos, así que hereda sus diferencias. Diciembre contra enero compara un pico de fiestas con un mes tranquilo. Usa el mismo periodo del año anterior para quitar la temporada, y usa solo periodos completos.
"Si la consulta corre y el total se ve bien, el patrón está bien"
Un total general puede estar bien mientras las filas mensuales están mal, por ejemplo cuando un total acumulado pasa de un año al siguiente. Revisa una fila que puedas verificar a mano: el primer mes de un año nuevo, la primera cohorte, el producto principal de una categoría.
"Un desfase de 12 en LAG siempre significa el año pasado"
Significa doce filas atrás. Si falta un mes, o si mezclas varias categorías sin PARTITION BY, doce
filas atrás puede ser otro mes o directamente otra categoría. Construye primero una cuadrícula completa
y particiona por aquello dentro de lo cual comparas.
Ponte a prueba
Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.
No del todo. El 57.6% compara enero con diciembre, el pico de fiestas. Eso mide sobre todo la temporada, no la salud del negocio.Prueba otra respuesta.
Correcto. Frente a enero de 2024 (235 pedidos), enero de 2025 (296) sube 26.0%. Usa año contra año cuando los datos son estacionales.
No del todo. El crecimiento MoM puede ser negativo. El número está bien; lo débil es la comparación.Prueba otra respuesta.
No del todo. Enero tiene solo tres días menos que diciembre. Eso explica unos pocos puntos como mucho, no una caída de 57.6%.Prueba otra respuesta.
Lección hecha: ya está marcada en el mapa.