Consultas avanzadas · jerarquías

CTE recursiva con WITH RECURSIVE

Aprende a recorrer estructuras padre-hijo cuando no conoces de antemano cuántos niveles necesitas.

Lectura: 8–10 minEjemplo comprobadoPráctica con pistas
Contenido de esta guía

Respuesta rápida

Una CTE recursiva permite que una consulta construya filas nuevas a partir de las filas que la propia CTE ya ha producido. Se utiliza para recorrer jerarquías, árboles, relaciones padre-hijo o secuencias cuya profundidad no conoces de antemano.

El patrón habitual tiene dos partes: un caso inicial que aporta las primeras filas y una parte recursiva que encuentra el siguiente nivel. La recursión termina cuando esa segunda parte deja de producir filas.

El modelo mental: raíz → siguiente nivel → repetir

Una CTE ordinaria nombra un resultado intermedio. Una CTE recursiva añade una relación consigo misma para avanzar por niveles. En un organigrama, por ejemplo, puedes empezar por la persona sin responsable, buscar después a quienes dependen de ella y repetir el proceso con cada nuevo nivel.

Modelo mental
filas iniciales
      ↓
filas del siguiente nivel
      ↓
filas del siguiente nivel
      ↓
... hasta que no aparezcan filas nuevas

La palabra RECURSIVE hace visible esa intención. PostgreSQL y SQLite documentan este patrón dentro de la cláusula WITH.

Estructura de WITH RECURSIVE

SQL
WITH RECURSIVE nombre_cte AS (
  -- Caso inicial
  SELECT ...

  UNION ALL

  -- Paso recursivo
  SELECT ...
  FROM tabla
  JOIN nombre_cte ON ...
)
SELECT ...
FROM nombre_cte;

Lee las dos ramas por separado. La primera define desde dónde empieza el recorrido. La segunda expresa cómo encontrar filas relacionadas con las que ya están en la CTE. UNION ALL reúne ambos conjuntos sin eliminar duplicados automáticamente.

La condición de avance es parte de la corrección. Una recursión sin una forma real de terminar puede repetir trabajo, alcanzar límites del motor o producir un recorrido incorrecto. En datos jerárquicos reales también debes pensar qué ocurriría ante un ciclo.

Ejemplo: recorrer el organigrama de empleados

La base educativa ya contiene empleados.responsable_id. Carmen no tiene responsable; Alex, Carlos y Gonzalo dependen de Carmen; y a partir de ellos aparecen niveles posteriores.

SQL comprobado en SQLite
WITH RECURSIVE organigrama AS (
  SELECT id,
         nombre,
         responsable_id,
         0 AS nivel,
         nombre AS ruta
  FROM empleados
  WHERE responsable_id IS NULL

  UNION ALL

  SELECT e.id,
         e.nombre,
         e.responsable_id,
         o.nivel + 1,
         o.ruta || ' > ' || e.nombre
  FROM empleados AS e
  JOIN organigrama AS o
    ON e.responsable_id = o.id
)
SELECT nombre, nivel, ruta
FROM organigrama
ORDER BY ruta;
Resultado completo con la base educativa
nombrenivelruta
Carmen Soler0Carmen Soler
Alex Ramos1Carmen Soler > Alex Ramos
Bea Molina2Carmen Soler > Alex Ramos > Bea Molina
Ismael Díaz2Carmen Soler > Alex Ramos > Ismael Díaz
Carlos Peña1Carmen Soler > Carlos Peña
Diana León2Carmen Soler > Carlos Peña > Diana León
Ernesto Cano2Carmen Soler > Carlos Peña > Ernesto Cano
Fátima Ríos2Carmen Soler > Carlos Peña > Fátima Ríos
Gonzalo Nieto1Carmen Soler > Gonzalo Nieto
Helena Costa2Carmen Soler > Gonzalo Nieto > Helena Costa

El caso inicial produce solo a Carmen. La rama recursiva usa cada fila obtenida para encontrar empleados cuyo responsable_id coincide con ese id. nivel + 1 registra la profundidad y ruta permite ver por qué camino se llegó a cada persona.

UNION ALL no significa “recursión infinita”

UNION ALL es habitual porque no obliga a deduplicar cada iteración. Pero la terminación no depende de cambiarlo mecánicamente por UNION: depende de que el paso recursivo tenga una relación que finalmente deje de producir filas y de que los datos no creen recorridos cíclicos no controlados.

En una jerarquía bien formada, una clave como responsable_id avanza hacia descendientes y llega a hojas que ya no tienen personas a su cargo. En grafos generales, la prevención de ciclos exige una estrategia explícita y puede variar según el motor y el problema.

Errores frecuentes

No separar el caso inicial del paso recursivo

Si no puedes explicar qué filas aparecen antes de la primera repetición, la consulta es difícil de verificar. Ejecuta mentalmente primero la rama inicial y después una sola iteración de la recursiva.

Unir en la dirección equivocada

Para bajar por un organigrama, la fila hija debe apuntar al identificador de la fila ya encontrada: e.responsable_id = o.id. Invertir esa relación cambia el sentido del recorrido.

Confundir el orden del recorrido con el orden del resultado

La recursión decide qué filas se generan. La presentación final sigue necesitando un ORDER BY si quieres un orden predecible.

Práctica guiada

Avanzada · CTE y jerarquías

Obtén solo el equipo que depende de Carlos Peña

Construye una CTE recursiva que empiece por el empleado con id = 4 y devuelva a Carlos y todos sus descendientes. Muestra nombre y nivel, donde Carlos está en nivel 0.

Criterio de éxito: aparecen Carlos Peña, Diana León, Ernesto Cano y Fátima Ríos.

Compatibilidad y límites

PostgreSQL y SQLite documentan CTE recursivas mediante WITH RECURSIVE. Las reglas de búsqueda, detección de ciclos, límites de recursión y optimización no son idénticas entre motores, así que una consulta de producción debe comprobarse en el motor concreto.

El motor educativo integrado de AprenderSQL.es enseña y ejecuta CTE ordinarias, pero no simula CTE recursivas. Los resultados de esta página se comprobaron con SQLite real sobre los mismos datos educativos, no con el laboratorio del curso.

Qué debes recordar

  • Una CTE recursiva combina un caso inicial con un paso que referencia la propia CTE.
  • La recursión termina cuando el paso recursivo deja de producir filas.
  • Las jerarquías padre-hijo son un caso de uso natural.
  • Los ciclos y la dirección de la relación deben tratarse explícitamente.
  • El orden final requiere ORDER BY si debe ser predecible.

Conceptos relacionados

Fuentes técnicas consultadas