1.9.

Vistas Recursivas en PostgreSQL – Actividad 5
Resolviendo el Problema del Agente Viajero con CTEs
Recursivas de PostgreSQL
Muchas implementaciones SQL no tienen bucles, haciendo muy difíciles
algunos tipos de análisis. PostgreSQL, SQL Server, y muchos otros tienen CTEs
recursivas!
Las usaremos para resolver el Problema del Agente Viajero y encontrar la ruta
más corta de ida y vuelta a través de varias ciudades de Colombia.

With Recursive
Las CTEs normales son muy buenas para ayudar a organizar consultas de gran
tamaño. Son una forma sencilla de hacer tablas temporales que se pueden
acceder más tarde con una consulta.

El miembro recursivo genera más filas para la CTE. el miembro recursivo se une con el 1 y muestra una segunda fila. el concepto como tal es muy similar a un bucle for en otros lenguajes de programación. Nótese cómo la CTE especifica su salida con el valor denominado prev_val. 2.miembro recursivo incrementer.prev_val + 1 from incrementer where prev_val < 10 -. Esto permite referirnos a la salida de la etapa recursiva anterior. A pesar de que nuestra CTE anterior crea muchas filas con el mismo valor. un miembro no-recursivo y un miembro recursivo. Estas CTEs tienen dos partes. El miembro no-recursivo selecciona el valor 1. . y luego uniéndose con las filas creadas en la recursión anterior. Aunque que eso puede sonar complicado. inicialmente uniéndose con las filas del miembro no-recursivo. sólo se devolverá un conjunto distinto de filas. En la tercera ejecución el paso recursivo se une con ambas filas 1 y 2 y añade la fila 2 (un duplicado) y 3.condición de terminación ) select * from incrementer. He aquí una CTE recursiva sencilla que genera los números del 1 al 10. hacen referencia a ellas mismas y permiten explorar datos jerárquicos.Las CTEs recursivas son más poderosas. En la segunda ejecución. La primera vez que la CTE recursiva se ejecuta genera una sola fila 1 usando el miembro no-recursivo.miembro no-recursivo union all select -. El miembro no-recursivo selecciona las filas de partida para los pasos recursivos. La CTE recursiva también devuelve solamente filas distintas. El miembro recursivo va después de una union o union all en la definición de la CTE. y el miembro recursivo agrega filas a él hasta el número 10: with recursive incrementer(prev_val) as ( select 1 -.

la base de datos está construyendo una tabla temporal llamada después de esta CTE recursiva utilizando uniones: Las CTE recursivas también pueden tener muchos parámetros.inc + 1.double * 2. vamos a ordenarlas para encontrar la más corta. Vamos a utilizar el más simple: la fuerza bruta. cruncher. 2. double.Y al final hay una condición de terminación para detener la recursión una vez la suma llega a 10.condición de terminación ) select * from cruncher. la CTE entraría en un bucle infinito! Por debajo. 2 y 3: with recursive cruncher(inc. square) as ( select 1.0. Nuestra CTE recursiva enumerará todas las rutas posibles y sus distancias totales.0 -. Encontrar el Camino más Corto Hay muchos algoritmos para encontrar el camino de ida y vuelta más corto a través de varias ciudades. Luego. .miembro recursivo cruncher. el doble y el cuadrado de los valores iniciales de 1.miembro no-recursivo union all select -.square ^ 2 from cruncher where inc < 10 -. ¡Sin esta condición. Con CTE recursivas podemos resolver el Problema del Agente Viajero. cruncher. 3. Aquí hay una que lleva la suma.

11.2110227 ).070275 as lat. . begin return sqrt(x * x + y * y). -75. -74.56457369999998 union all select 'Villavicencio'. 10.63769049999996 union all select 'Santa Marta'. -75. -73. 6.4742449.2403547.348903. -75. -75.8890971. -72. lon1 float. lon2 float ) returns float as $$ declare x float = 111. -73. -72. 4.40052300000002 union all select 'Cartagena'.47942569999998 union all select 'Ibagué'. 10.444675999999999. 5. 5.En primer lugar.215).12 * (lon2 .24363349999999 union all select 'Medellín'.253040800000001. y float = 111. tenemos la lista de ciudades con los clientes de la organización.1513822.49668959999997 union all select 'Yopal'. junto con sus latitudes y longitudes: create table places as ( select 'Manizales' as name. 7. 4. Y necesitaremos una función para calcular que tan distantes están dos puntos de latitud / longitud en kilómetros (gracias a strkol): create or replace function lat_lon_distance( lat1 float.lon1) * cos(lat1 / 92.24243799999999 union all select 'Valledupar'.lat1).12 * (lat2 . lat2 float.51381659999998 as lon union all select 'Cúcuta'.3910485.

num_places) as ( select -. lat. 1 from places where name = 'Manizales' union all select -. places. 0::float.miembro no-recursivo name.end.places_chain) = 0 ) -. last_lon.total_distance + lat_lon_distance(last_lat.lon).adiciona al actual places_chain travel.lon. last_lat. -. $$ language plpgsql. travel where position(places.lat. lon. total_distance.places_chain || ' -> ' || places.miembro recursivo -. last_lon. Nuestra CTE usará Manizales como su ciudad de miembro no-recursivo.name in travel.num_places + 1 from places. places. places. places.NO EJECUTAR AÚN! Los parámetros en la CTE son: . travel.adiciona al actual total_distance travel.name.lat. y luego recursivamente ir desde allí a todas las demás ciudades: with recursive travel(places_chain.

pero un join es más elegante. hacemos esto adicionando a la sentencia anterior WITH la siguiente sentencia: select travel. position(. lo que invalida esta instancia de la recursividad.num_places = 9 and places. places. Tenemos que añadir la distancia desde la última ciudad de regreso a Manizales para completar la ida y vuelta..ascendentemente! limit 1.) devolverá un número mayor que 0.. Podemos ver todas las rutas posibles adicionando a la sentencia anterior WITH la selección de las visitas a las 9 ciudades: select * from travel where num_places = 9.lon) as final_dist from travel. travel.last_lat. places where travel. ya que todas las ciudades fueron visitadas En el miembro recursivo. usaremos esto para informarle qué rutas se han completado. la base de datos está haciendo las mismas cosas por . Si ya hemos visitado Yopal. A pesar de que esta consulta es mucho más complicada que la consulta anterior incrementer.    places_chain: La lista de lugares visitados hasta el momento. total_distance + lat_lon_distance( travel.name. Podríamos “quemar” la latitud / longitud de Manizales.last_lon.lat.places_chain || ' -> ' || places. places. que será diferente para cada instancia de la recursividad last_lat y last_lon: La latitud y la longitud del último lugar de la places_chain total_distance: La distancia recorrida yendo de un lugar a otro en el places_chain num_places: El número de lugares en places_chain. Una vez que hemos hecho esto debemos ordenar por distancia y mostrar solo la menor. la cláusula where asegura que nunca repetimos un lugar.name = 'Manizales' order by final_dist ASC -.

la rama inferior es el join final y el ordenamiento: Ejecuta esta consulta y verás que la ruta más corta toma 1926 kilómetros y visita las ciudades en este orden: Manizales -> Medellín -> Cartagena -> Santa Marta -> Valledupar -> Cúcuta -> Yopal -> Villavicencio -> Ibagué -> Manizales .debajo. La rama superior es la creación de las filas de la CTE.

podemos resolver el Problema del Agente Viajero en SQL! .Cambiando el ordenamiento de la consulta de manera descendente verás que la ruta más larga toma 5497 kilómetros y visita las ciudades en este orden: Manizales -> Cúcuta -> Medellín -> Yopal -> Cartagena -> Villavicencio -> Santa Marta -> Ibagué -> Valledupar -> Manizales ¡Gracias a las CTE recursivas.