Ir al contenido
wannacode

Árboles y secuencias: WITH RECURSIVE

En resumen: WITH RECURSIVE es una consulta con un paso que lee su propio resultado, así que puede recorrer árboles como las categorías o generar una secuencia de fechas.

Problema

La ruta hasta la categoría

Encima de cada producto el sitio web muestra la ruta hasta él: «Alimentos > Lácteos». Las categorías son un árbol: cada una tiene un parent_id que apunta a la categoría de arriba. Con un self join del módulo sobre unir tablas se hace así:

SQLbase de datos: tienda

Esto funciona porque el árbol tiene dos niveles. Si mañana aparece «Quesos» bajo «Lácteos», necesitas un join más, y para cuatro niveles otro más. No sabes de antemano cuántos niveles habrá.

La idea

Un paso que se lee a sí mismo

WITH RECURSIVE permite que un paso lea sus propias filas. Este es el ejemplo más pequeño, los números del 1 al 5:

SQLbase de datos: tienda

El paso tiene dos partes, unidas por UNION ALL:

  1. El inicio SELECT 1 da la primera fila.
  2. La parte recursiva SELECT x + 1 FROM n WHERE x < 5 toma las filas de la ronda anterior y crea otras nuevas: de 1 hace 2, de 2 hace 3.

Esto se repite hasta que la parte recursiva no devuelve nada: con x = 5 la condición ya no es verdadera. Un paso así se llama CTE recursiva.

Sin WHERE x < 5 la consulta no pararía nunca. Toda parte recursiva necesita una condición que tarde o temprano la detenga.

La idea

El árbol de categorías

Ahora la ruta. El inicio son las categorías principales (sin padre). La parte recursiva encuentra los hijos de todo lo encontrado hasta ahora y añade su nombre a la ruta:

SQLbase de datos: tienda

En la primera ronda están «Alimentos» y «Hogar» (nivel 1). En la segunda están las seis subcategorías (nivel 2), cada una con la ruta de su padre, « > » y su propio nombre. En la tercera ronda ya no hay hijos y todo se detiene. Si el árbol se hace más profundo, la misma consulta llegará también a los niveles nuevos.

Pruébalo

Todo lo de «Alimentos»

Muestra el nombre y el precio de todos los productos de «Alimentos», junto con sus subcategorías. Obtén los números de categoría con un paso recursivo, no a mano. El campo empieza con el inicio del paso.

SQLbase de datos: tienda

Míralo

Los días sin ventas

La gerencia quiere un gráfico de las ventas por día de octubre de 2026. El primer intento:

SQLbase de datos: tienda

Cinco filas. Octubre tiene 31 días, y el gráfico uniría el 3 de octubre directamente con el 5 y escondería que del 7 al 17 de octubre no se vendió nada. Un día sin pedidos no está en orders, así que GROUP BY no tiene de qué crear una fila.

La idea

Una secuencia de fechas

Un paso recursivo puede crear todos los días por sí solo. El inicio es el 1 de octubre, y cada ronda añade un día con date(day, '+1 day'):

SQLbase de datos: tienda

31 filas. Ahora las ventas se unen a los días con LEFT JOIN, y para los días sin pedidos COALESCE pone 0:

SQLbase de datos: tienda

Están los 31 días, también los que tienen cero. El orden del LEFT JOIN importa: a la izquierda están los días, de los que quieres todos, a la derecha los pedidos, que pueden faltar.

Más precisamente, para curiosos

PostgreSQL tiene una función ya hecha para una secuencia: generate_series(DATE '2026-10-01', DATE '2026-10-31', INTERVAL '1 day'). En SQLite y en MySQL (desde la versión 8) una secuencia así se hace con WITH RECURSIVE, como aquí.

Ejercicio

Dos tareas

1. ¿En cuántos días de octubre de 2026 la tienda no tuvo ningún pedido?

Cuenta los días de octubre de 2026 sin un solo pedido, hasta el final del mes, incluidos los días posteriores al último pedido (columna quiet_days). El campo ya tiene la secuencia de días.

SQLbase de datos: tienda

2. El sitio web quiere la ruta completa de cada producto: «Alimentos > Lácteos > Queso blanco».

Muestra el nombre de cada producto que tiene categoría y su ruta completa (columna breadcrumb), desde la categoría principal hasta el nombre del producto. El campo ya tiene el árbol.

SQLbase de datos: tienda

Test

Ponte a prueba

1. ¿Qué dos partes tiene un paso recursivo?
2. ¿Qué pasa si quitas WHERE x < 5 de la parte recursiva?
3. Un informe por días muestra solo 5 días de octubre. ¿Por qué?
4. Para la ruta «Alimentos > Lácteos», ¿por qué el paso recursivo es mejor que un self join?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·