Módulo 5 · Consultas avanzadas

Práctica: domina funciones de ventana

Convierte rankings y comparaciones en consultas comprobables con ROW_NUMBER(), PARTITION BY y agregaciones con OVER, sin perder el detalle de cada pedido.

35 minNivel intermedioAntes necesitas: CTE y funciones de ventana

0 de 45 lecciones

Al terminar, podrás

  • Elegir qué filas forman cada partición de una función de ventana.
  • Construir un ranking estable con ROW_NUMBER() y un desempate explícito.
  • Usar una agregación con OVER para comparar cada fila con un total sin agruparla.
  • Calcular primero una ventana en una CTE y filtrar después cuando necesitas el primer elemento de cada grupo.

Recupera lo aprendido: ¿Dónde debe ir el filtro posicion = 1 cuando posicion se calcula con una ventana?

La idea clave: define primero la ventana y después el cálculo

En esta práctica trabajarás con la tabla pedidos. Para cada problema separa dos decisiones: qué filas pertenecen al mismo grupo y qué cálculo quieres hacer sobre ese grupo. Esa separación evita uno de los errores más frecuentes: escribir una función de ventana correcta sintácticamente pero con una partición u orden que no representa la pregunta.

Una comprobación útil

Antes de mirar el número calculado, lee el PARTITION BY y el ORDER BY como una frase. Por ejemplo: «para cada cliente, ordena sus pedidos de mayor a menor total». Si esa frase no coincide con la pregunta, la ventana tampoco.

Ejemplo guiado: numera los pedidos de cada cliente

Queremos asignar la posición 1 al pedido de mayor importe de cada cliente. El identificador del pedido sirve como segundo criterio para que, si dos pedidos tienen el mismo total, la numeración siga siendo determinista.

SQL
SELECT cliente_id,
       id AS pedido_id,
       total,
       ROW_NUMBER() OVER (
         PARTITION BY cliente_id
         ORDER BY total DESC, id
       ) AS posicion
FROM pedidos
ORDER BY cliente_id, posicion;

El cliente 1 conserva sus tres pedidos y recibe las posiciones 1, 2 y 3: los pedidos 111, 101 y 103, respectivamente. El cliente 2 empieza otra vez en 1 porque PARTITION BY cliente_id reinicia la numeración para cada cliente.

Comprueba tu comprensión

Si quitas PARTITION BY cliente_id, ¿qué cambia?

Mantén el detalle mientras calculas un total por cliente

Una agregación tradicional con GROUP BY cliente_id devolvería una fila por cliente. Con una agregación de ventana puedes conservar cada pedido y añadir, en cada fila, el total acumulado de su cliente:

SQL
SELECT id AS pedido_id,
       cliente_id,
       total,
       SUM(total) OVER (
         PARTITION BY cliente_id
       ) AS total_cliente
FROM pedidos
ORDER BY cliente_id, id;

Para el cliente 1 aparecen sus tres pedidos y, junto a cada uno, 402.3 como total_cliente. El cálculo resume el grupo, pero la consulta no reduce las tres filas a una sola. Esa diferencia es una de las razones principales para usar ventanas en informes analíticos.

Error habitual: filtrar la posición demasiado pronto

No uses la ventana directamente en WHERE en la misma capa

WHERE filtra antes de que se calculen las funciones de ventana en PostgreSQL, MySQL y SQLite. Si quieres quedarte con posicion = 1, calcula primero ROW_NUMBER() en una CTE o subconsulta y filtra en la consulta exterior.

Laboratorio: devuelve el pedido de mayor importe de cada cliente

Ahora integra CTE, ROW_NUMBER(), PARTITION BY y un filtro exterior. La consulta debe devolver una fila por cliente: el pedido con mayor total. En caso de empate, usa el menor id como desempate.

Laboratorio SQL

El pedido principal de cada cliente

Pendiente

Usa una CTE para calcular ROW_NUMBER() por cliente_id, ordenando los pedidos por total descendente e id ascendente como desempate. Incluye todos los estados. En la consulta exterior conserva posicion = 1 y devuelve cliente_id, pedido_id y total, ordenados por cliente_id.

Resultado esperado: 10 filas con cliente_id, pedido_id y total, ordenadas por cliente_id.

Ver tablas y datos

Cargando la estructura del ejercicio…

La consulta usa datos ficticios y se ejecuta solo en este navegador.

Ronda autónoma: detecta los dos pedidos más altos por cliente

Intermedia · ventanas + filtro exterior

Amplía el ranking sin cambiar la ventana

Reutiliza la consulta anterior para devolver como máximo los dos pedidos de mayor importe de cada cliente. Conserva también la columna posicion y ordena por cliente_id, posicion.

Si te bloqueas, separa la consulta en dos capas

  1. Escribe la consulta interior sin filtrar posiciones.
  2. Comprueba que cada cliente empieza en 1.
  3. Revisa que el desempate esté dentro del ORDER BY de la ventana.
  4. Envuelve ese resultado en una CTE.
  5. Filtra posicion desde la consulta exterior.

Si todavía dudas sobre OVER o PARTITION BY, vuelve a la lección de funciones de ventana. Si lo que te bloquea es la consulta por etapas, repasa CTE con WITH. Para el caso específico de filtrar una ventana demasiado pronto, consulta función de ventana en WHERE.

Fuentes técnicas consultadas

La práctica utiliza sintaxis de ventana soportada por PostgreSQL, MySQL 8 y SQLite: OVER, PARTITION BY, orden dentro de la ventana y ROW_NUMBER(). Los tres motores mantienen el detalle de las filas al aplicar funciones de ventana.

Comprueba si puedes explicarlo

Si filtras los pedidos cancelados antes de ROW_NUMBER, ¿cambia el significado de «pedido mayor por cliente»?

Ver respuesta razonada

Sí. Obtienes el mayor entre los pedidos no cancelados. Si numeras primero y luego descartas cancelados, puedes perder un cliente cuyo pedido mayor estaba cancelado.

Responde antes de abrir la explicación. Si necesitaste ayuda, cierra la respuesta y vuelve a explicarlo con tus palabras antes de dar la lección por aprendida.

Qué debes recordar

  • PARTITION BY define dónde vuelve a empezar el cálculo.
  • El ORDER BY dentro de OVER determina la posición, no necesariamente el orden final del resultado.
  • Una agregación con OVER puede resumir un grupo sin eliminar las filas individuales.
  • Para filtrar por una posición calculada, calcula la ventana en una capa y filtra en la siguiente.

A continuación: pasarás del análisis de filas a modificar datos con INSERT.

Conceptos relacionados