Skip to content
Empieza gratis

¿Cómo numero las filas de cada grupo con ROW_NUMBER?

Numera los pedidos de cada cliente del primero al último con ROW_NUMBER, y desempata los del mismo día.

Curso gratis. Tu Maestro, una IA, .

La fonda también recibe encargos: la tabla pedidos tiene 60, de seis clientes frecuentes, de enero a marzo de 2025 (datos sintéticos). ¿Cuál fue el primer pedido de cada cliente? ¿Y el tercero? Para contestar, necesitas un número en cada fila que diga "este es el pedido 1, 2, 3… de este cliente". Eso hace ROW_NUMBER():

  • PARTITION BY cliente: cada cliente empieza a contar desde 1.
  • ORDER BY fecha dentro de OVER: en qué orden se cuentan sus pedidos.

Con SUM en el módulo anterior no hacía falta orden: el total es el mismo como sea que lleguen las filas. Para numerar sí hace falta. Sin ORDER BY dentro de OVER, el número que le toca a cada fila no está bien definido.

SQL · línea 3
ROW_NUMBER() OVER (
  PARTITION BY cliente
  ORDER BY fecha, pedido_id
) AS num_pedido
La línea 3 trae un desempate: si dos pedidos caen el mismo día, va primero el de pedido_id menor.

¿Por qué pedido_id en la línea 3? Porque Elena tiene dos pedidos el 6 de febrero de 2025, el 1014 y el 1015. Con ORDER BY fecha sola, nada en tu consulta dice cuál va primero, así que no cuentes con que salgan siempre igual. Agregar pedido_id deja un orden único.

Inténtalo

Numera los pedidos de cada cliente, del más antiguo al más reciente. Regresa cliente, pedido_id, fecha, monto y num_pedido, y ordena el resultado por cliente, num_pedido. Antes de escribirla, contesta la pregunta de abajo. Si los números no reinician en cada cliente, pregúntale a tu Maestro.

Comprueba lo que sabes: Antes de correr: imagina la consulta sin el desempate, con ORDER BY fecha sola. ¿Qué números reciben los pedidos 1014 y 1015 de Elena, que son del mismo día?

Numera los pedidos de cada cliente en orden de fecha, y desempata los del mismo día con pedidoid. Regresa cliente, pedidoid, fecha, monto y numpedido, ordenado por cliente, numpedido. Terminaste cuando ves 60 filas, cada cliente va del 1 al 10 y los pedidos 1014 y 1015 de Elena s

Tu turno

Numera los pedidos de cada cliente en orden de fecha, y desempata los del mismo día con pedido_id. Regresa cliente, pedido_id, fecha, monto y num_pedido, ordenado por cliente, num_pedido. Terminaste cuando ves 60 filas, cada cliente va del 1 al 10 y los pedidos 1014 y 1015 de Elena son el 4 y el 5.

Tablas: pedidos (pedido_id, cliente, fecha, monto)

pedidos · 60 filas

ColumnaTipo
pedido_id Número del pedido; crece con el orden en que se registró.BIGINT
cliente Nombre del cliente.VARCHAR
fecha Día del pedido, sin hora.DATE
monto Monto del pedido, en pesos.BIGINT

Primeras filas

pedido_idclientefechamonto
1001Fer2025-01-03850
1002Diego2025-01-041360
1003Beto2025-01-11600

Cada ejecución empieza con copias nuevas de las tablas del curso, así que puedes probar lo que quieras.

Listo para ejecutar tu consulta.

Ejecútalo tú

Fíjate en que la consulta tiene dos ORDER BY, y hacen trabajos distintos:

  • El de adentro de OVER (...) decide en qué orden se numera.
  • El del final decide en qué orden ves las filas.

A veces el resultado sale ordenado aunque no pongas el del final, pero eso es suerte, no una promesa. Si te importa el orden en que ves las filas, escríbelo al final.

Fila por fila

Así quedaron los primeros seis pedidos de Elena con la solución, en el sandbox de Maestro. La partición de Elena son sus 10 pedidos; ROW_NUMBER() la recorre en el orden de fecha, pedido_id y le pone a cada fila su lugar:

pedido_idfechamontonum_pedido
10042025-01-125301
10072025-01-1822702
10092025-01-2312803
10142025-02-0623904
10152025-02-0615805
10282025-02-218906

Cuando la consulta llega a la primera fila de Fer, la cuenta vuelve a 1. Eso es todo lo que hace PARTITION BY aquí: decide dónde reinicia la cuenta. Sin él, los 60 pedidos irían del 1 al 60 en una sola fila larga.

Tres casos que te van a pasar

Del más reciente al más antiguo. ORDER BY fecha DESC, pedido_id DESC pone el número 1 en el pedido más nuevo. Así se responde "el último" en lugar de "el primero", y es la base de la página que sigue.

Una fecha vacía. Si una fila tiene fecha en NULL, cae en algún lugar de la cuenta. En DuckDB los NULL van al final, en orden ascendente y también en descendente: lo probamos en el sandbox con una columna que tenía un NULL, y quedó en el último lugar en los dos sentidos. PostgreSQL no hace lo mismo: su documentación dice que con DESC pone los NULL primero. Si numeras "del más reciente al más antiguo" en PostgreSQL y hay fechas vacías, el número 1 puede ser una fila sin fecha. Cuando importe, escribe NULLS LAST y deja de depender del motor.

Un orden que se repite. Si las columnas de tu ORDER BY pueden repetirse, agrega una que no se repita, como el identificador. Sin ella, otra corrida u otro motor pueden darte otro número, y nada te avisa.

La versión de entrevista: "Para cada cliente, ¿cuál fue su segunda compra?" o "numera las transacciones de cada usuario en orden cronológico". Lo que se evalúa es que pongas PARTITION BY en la columna correcta, que el ORDER BY de adentro tenga dirección y desempate, y que sepas que quedarte con el número 2 no se hace en el WHERE (eso es la página que sigue). Si quieres practicar cómo lo explicarías en voz alta, pídele a tu Maestro que te lo pregunte.

Fuera de las entrevistas, el uso más común de ROW_NUMBER() es quitar duplicados. Si una tabla se cargó dos veces y el mismo cliente aparece en varias filas, numéralas por cliente, de la carga más reciente a la más vieja, y quédate con la 1:

SQL · líneas 3–4
SELECT *,
       ROW_NUMBER() OVER (
         PARTITION BY cliente_id
         ORDER BY cargado_en DESC, archivo DESC  -- con desempate
       ) AS n
FROM clientes_cargados;
El patrón para quitar duplicados, sobre una tabla tuya: cada repetido recibe un número y te quedas con n = 1. Cómo quedarte con la 1 lo ves en la página que sigue.

Comprueba lo que sabes: En ROW_NUMBER() OVER (PARTITION BY cliente ORDER BY fecha), ¿qué hace el ORDER BY que está dentro de OVER?