Volver a la lista de artículos Artículos
Lectura de 19 minutos

11 Ejercicios de Expresiones Comunes de Tabla SQL

En este artículo, le ofrecemos 11 ejercicios prácticos que ponen en práctica sus conocimientos sobre las expresiones comunes de tabla (ETC). Cada ejercicio CTE va acompañado de una solución y una explicación detallada.

Como dice el refrán, "un pequeño progreso cada día da grandes resultados". Y eso es indudablemente cierto para el dominio de SQL. Del mismo modo que debería ir al gimnasio con regularidad para mantener los músculos tonificados, debería hacer ejercicios de expresiones comunes de tabla con frecuencia para mantener sus habilidades de consulta tonificadas para trabajos de análisis de datos pesados.

Las CTE, o expresiones comunes de tabla, son potentes sentencias de SQL. Permiten definir temporalmente una subconsulta dentro de una consulta SQL y asignarle un nombre. Esto significa que se puede hacer referencia a la CTE por su nombre dentro de la consulta, como si fuera una tabla.

El valor de los CTE

Además de los nombres, existen otras diferencias importantes entre las CTE y las subconsultas. Las CTEs son útiles para estructurar consultas complejas, haciéndolas más fáciles de leer, entender y depurar. Las CTEs también pueden utilizarse repetidamente dentro de la misma consulta, haciéndola más concisa. Estas cualidades de las CTEs las hacen ideales para el trabajo de análisis de datos, ya que es común que las consultas de análisis de datos mezclen SELECTs de datos simples con SELECTs agrupados con totales, promedios y otras funciones agregadas. Sin las CTE, las consultas complejas podrían llegar a ser prácticamente imposibles de leer o requerir la creación de tablas o vistas temporales que poblaran innecesariamente la base de datos con montones de objetos.

Las CTE también son muy útiles a la hora de realizar análisis de datos muy complejos sin recurrir a otros lenguajes de programación. Utilizar la recursividad para encontrar caminos críticos o recorrer tipos de datos abstractos (como árboles y grafos) son ejemplos que demuestran la utilidad de las CTE. También lo es la posibilidad de crear consultas anidadas, que reducen gradualmente la complejidad de un problema hasta convertirlo en un simple SELECT.

La capacidad de las CTE para hacer las consultas más legibles y concisas es algo que apreciará si necesita revisar una consulta larga años después de escribirla. Afortunadamente, la mayoría de los sistemas modernos de gestión de bases de datos relacionales (RDBMS) - incluyendo PostgreSQL, MySQL, SQL Server y Oracle - permiten el uso de CTEs.

Todos los ejercicios recopilados en este artículo se han extraído de nuestro Consultas recursivas y expresiones de tabla comunes curso. Se trata de un tutorial en profundidad sobre las expresiones comunes de tabla en el análisis de datos. Los 114 ejercicios interactivos cubren CTEs simples, CTEs anidadas y CTEs recursivas en un tiempo total estimado de 18 horas. También puede obtener más información sobre las ETC leyendo Explicación de las ETC con ejemplos.

Otro tema importante que todo analista de datos debe dominar son las funciones de ventana SQL. Puede consultar este conjunto de ejercicios de práctica de funciones de ventana SQL para poner a prueba sus habilidades.

Ahora, comencemos con nuestros ejercicios de expresiones comunes de tabla. Comenzaremos con CTEs simples y luego pasaremos a CTEs anidadas y recursivas.

Ejercicios CTE Simples

Para estos ejercicios de CTE, utilizaremos una base de datos diseñada para gestionar proyectos de crowdfunding. Este esquema se compone de tres tablas:

  1. supporter contiene información sobre los simpatizantes, que son aquellos que donan dinero para los proyectos.
  2. project contiene información sobre los proyectos que reciben donaciones de los simpatizantes.
  3. donation registra las donaciones de los simpatizantes a los proyectos.

La tabla supporter almacena los datos id, first_name, y last_name de cada simpatizante del sistema. Veamos algunas de sus filas:

idfirst_namelast_name
1MarleneWagner
2LonnieGoodwin
3SophiePeters
4EdwinPaul
5HughThornton

La tabla project almacena id, category, author_id, y minimal_amount necesarios para poner en marcha cada proyecto. Estas son algunas de sus filas:

idcategoryauthor_idminimal_amount
1music11677
2music521573
3traveling24952
4traveling53135
5traveling28555

Los datos de la columna author_id vinculan cada proyecto de la project con una fila de la tabla supporter tabla. Cada partidario relacionado con un proyecto por la columna author_id es el autor de ese proyecto.

Por último, la donation tabla contiene id, supporter_id, el importe de la donación, y la columna donated, que muestra la fecha en que se realizó cada donación.

idproject_idsupporter_idamountdonated
144928.402016-09-07
2818384.382016-12-16
3612367.212016-01-21
4219108.622016-12-29
51020842.582016-11-30

Ejercicio 1: Uso de una CTE para obtener datos totalizados

Ejercicio: Obtener el ID del proyecto, el importe mínimo y el total de donaciones de los proyectos que han recibido donaciones superiores al importe mínimo.

Solución:

WITH project_revenue AS (
  SELECT
    project_id,
    SUM(amount) AS sum_amount
  FROM donation
  GROUP BY project_id
)
SELECT project.id, minimal_amount, sum_amount
FROM project_revenue
INNER JOIN project ON
project.id = project_revenue.project_id
WHERE sum_amount >= minimal_amount;

Explicación: Para resolver este ejercicio, utilizamos un CTE llamado project_revenue que totaliza las donaciones de cada proyecto. Este CTE tiene dos columnas: id y sum_amount, siendo esta última la suma calculada de donaciones para cada project_id. Tras la definición del CTE, utilizamos una sentencia SELECT que une la tabla project con el CTE. Para cada proyecto que ha recibido donaciones, el CTE devuelve su id, minimal_amount, y el total de donaciones (sum_amount) que ha recibido.

El CTE project_revenue sólo incluye las filas de los proyectos que han recibido donaciones porque obtiene los datos de la tabla de donaciones. El SELECT debajo de la definición del CTE también muestra sólo los proyectos que recibieron donaciones debido a la INNER JOIN entre el CTE y la project tabla. Y la condición WHERE asegura que obtendremos sólo los proyectos para los que la cantidad donada exceda la cantidad mínima.

Si necesita practicar la agrupación de datos en SQL, consulte este conjunto de 10 ejercicios GROUP BY. Pruebe estos ejercicios de práctica de SQL avanzado para acelerar su camino hacia el dominio de SQL.

Ejercicio 2: Uso de Múltiples CTEs en la Misma Sentencia

Ejercicio: Seleccionar los colaboradores que donaron más de 200 $ en total o que donaron al menos dos veces.

Solución:

WITH rich AS (
  SELECT
	s.id,
	first_name,
	last_name
  FROM supporter s
  JOIN donation d
	ON d.supporter_id = s.id
  GROUP BY s.id, first_name, last_name
  HAVING SUM(amount) > 200
),
frequent AS (
  SELECT
	s.id,
	first_name,
	last_name
  FROM supporter s
  JOIN donation d
	ON d.supporter_id = s.id
  GROUP BY s.id, first_name, last_name
  HAVING COUNT(d.id) > 1
)
SELECT
  id,
  first_name,
  last_name
FROM rich
UNION ALL
SELECT
  id,
  first_name,
  last_name
FROM frequent;

Explicación: Este ejercicio nos pide que combinemos dos resultados diferentes que debemos obtener recuperando información de los campos donation y supporter los simpatizantes cuyas donaciones totales superan los 200 $ y los simpatizantes que realizaron más de una donación. Esta situación es ideal para resolverla escribiendo dos CTEs, una para obtener el primer conjunto de datos (rich) y la otra para obtener el segundo conjunto (frequent).

La sintaxis SQL permite escribir múltiples CTEs dentro del mismo comando, lo que aprovechamos para resolver este ejercicio. Al colocar cada subconsulta en un CTE diferente, el SELECT final es simplemente la unión de dos SELECTs simples - cada uno de los cuales obtiene los datos directamente de un CTE.

CTE anidados

Aunque ningún RDBMS permite la creación de un CTE dentro de otro CTE, lo que sí permiten son CTEs anidados; esto es cuando un CTE hace referencia a un CTE previamente definido como si fuera una tabla. De esta forma, los CTEs crean diferentes niveles de abstracción. Esto hace que la consulta final sea sencilla y concisa SELECT.

Para nuestros ejercicios de CTE anidadas, utilizaremos un esquema de tablas de una empresa de ventas puerta a puerta. Este esquema tiene tres tablas: salesman, daily_salesy city. La tabla salesman incluye los datos id, first_name, last_name, y city_id de cada vendedor. Estas son algunas de sus filas:

idfirst_namelast_namecity_id
1FrederickWebster1
2CaseySantiago2
3CindyFields3
4TimothyPratt4
5SusanRose5

La tabla daily_sales tabla representa las ventas totalizadas por día y vendedor. Tiene las columnas day, salesman_id, items_sold, amount_earned, distance, y customers. Las dos últimas columnas muestran la distancia recorrida y el número de clientes atendidos por cada vendedor cada día. Estas son algunas de sus filas:

daysalesman_iditems_soldamount_earneddistancecustomers
2017-01-15101673.203020
2017-01-152162288.4913613
2017-01-153171232.7812914
2017-01-15421496.882512
2017-01-155221384.1334018

Por último, tenemos la city tabla que almacena los id, name, country, y region de cada ciudad:

idnamecountryregion
1ChicagoUSAAmericas
2New YorkUSAAmericas
3Mexico CityMexicoAmericas
4Rio de JaneiroBrasilAmericas
5ParisFranceEurope

Ejercicio 3: Uso de CTEs Anidados para Avanzar Incrementalmente hacia un Resultado

Ejercicio: Obtener la fecha, el ID de la ciudad, el nombre de la ciudad y el importe total de todas las ventas diarias - agrupadas por fecha y ciudad - que superen la media de ventas diarias de todas las ciudades y todos los días.

Solución:

WITH earnings_per_day_city AS (
  SELECT
	ds.day,
	c.id,
	c.name,
	SUM(amount_earned) AS total_earnings
  FROM salesman s
  JOIN daily_sales ds
	ON s.id = ds.salesman_id
  JOIN city c
	ON s.city_id = c.id
  GROUP BY ds.day, c.id, c.name
),
overall_day_city_avg AS (
  SELECT
	AVG(total_earnings) AS avg_earnings
  FROM earnings_per_day_city
)
SELECT
  day,
  id,
  name,
  total_earnings
FROM earnings_per_day_city, overall_day_city_avg
WHERE total_earnings > avg_earnings;

Explicación: Los CTEs anidados nos permiten dividir un problema en partes y aproximarnos gradualmente a la solución. En este ejercicio, primero necesitamos totalizar las ventas por día y por ciudad. Lo hacemos con el primer CTE, earnings_per_day_city.

Después necesitamos obtener una media de todas las ventas totalizadas por día y por ciudad. Lo conseguimos con el CTE overall_day_city_avg, que a su vez utiliza los resultados totalizados previamente por el CTE earnings_per_day_city. Este segundo CTE devolverá una única fila con la media de las ventas de todos los días y todas las ciudades.

En el SELECT final, simplemente tomamos los datos de los dos CTE (no es necesario combinarlos con un JOIN, ya que earnings_per_day_city devuelve una única fila) y añadimos la condición WHERE de que el total de ventas del día y la ciudad debe ser mayor que la media global.

Ejercicio 4: Combinación de Consultas y Subconsultas Anidadas

Ejercicio: Obtener la fecha en la que la media de clientes atendidos por región fue la más baja de todas, mostrando esta media junto con la fecha.

Solución:

WITH sum_region AS (
  SELECT
	day,
	region,
	SUM(customers) AS sum_customers
  FROM salesman s
  JOIN daily_sales ds
	ON s.id = ds.salesman_id
  JOIN city c
	ON s.city_id = c.id
  GROUP BY day, region
),
avg_region AS (
  SELECT
	day,
	AVG(sum_customers) AS avg_region_customers
  FROM sum_region
  GROUP BY day
)
SELECT
  day,
  avg_region_customers
FROM avg_region
WHERE avg_region_customers = (SELECT
  MIN(avg_region_customers)
  FROM avg_region);

Explicación: Para resolver esta consulta utilizamos la misma aproximación sucesiva a la solución que en el ejercicio anterior, creando primero un CTE para obtener el número total de clientes servidos por día y por región y luego otro CTE basado en el anterior para obtener las medias diarias de clientes servidos por día. A continuación, en el SELECT final, utilizamos una subconsulta para obtener el mínimo del número medio de clientes por día y lo utilizamos en la cláusula WHERE como valor de comparación, de forma que la consulta devuelva el día que corresponde a ese mínimo.

Si quisiera dividir aún más el SELECT final, podría añadir un tercer CTE en lugar de una subconsulta. De esta forma, el SELECT final se simplifica aún más. Aquí está el nuevo (tercer) CTE y el externo SELECT:

min_avg_region as (
  SELECT
    MIN(avg_region_customers) as min_avg_region_customers
  FROM avg_region
  )
SELECT
  day,
  avg_region_customers
FROM avg_region, min_avg_region
WHERE avg_region_customers = min_avg_region_customers;

Ejercicio 5: Uso de CTEs Anidados para Calcular Estadísticas Complejas

Ejercicio: Para cada ciudad, calcule la distancia total promedio recorrida por cada vendedor. También calcule un promedio general de los promedios de todas las ciudades.

Solución:

WITH distance_salesman_city AS (
  SELECT
	city_id,
	salesman_id,
	SUM(distance) AS sum_distance
  FROM daily_sales d
  JOIN salesman s
	ON d.salesman_id = s.id
  GROUP BY city_id, salesman_id
),
city_average AS (
  SELECT
	city_id,
	AVG(sum_distance) AS city_avg
  FROM distance_salesman_city
  GROUP BY city_id
)
SELECT AVG(city_avg)
FROM city_average;

Explicación: Las ventajas de los CTEs anidados son notables cuando se necesita realizar cálculos estadísticos compuestos de varios pasos sucesivos.

En este caso, el resultado final es una media total de las medias por ciudad de las sumas de distancias de cada ciudad y vendedor. Es decir, una media de medias de sumas. Los CTEs nos permiten hacer una aproximación por pasos al resultado, de forma análoga a como lo haría un científico de datos con fórmulas estadísticas.

Ejercicio 6: Uso de CTEs Anidados para Comparar Conjuntos de Elementos

Ejercicio: Comparar la media de ventas de todos los vendedores de EEUU con la media de ventas de todos los vendedores del resto del mundo.

Solución:

WITH cities_categorized AS (
  SELECT
    id AS city_id,
    CASE WHEN country = 'USA' THEN country ELSE 'Rest of the World' END AS category
  FROM city
),
sales_category AS (
  SELECT
    category,
    salesman_id,
    SUM(items_sold) total_sales
  FROM daily_sales ds
  JOIN salesman s
    ON s.id = ds.salesman_id
  JOIN cities_categorized
    ON cities_categorized.city_id = s.city_id
  GROUP BY category, salesman_id
)

SELECT
  category,
  AVG(total_sales)
FROM sales_category
GROUP BY category;

Explicación: En el primer CTE (cities_categorized), separamos las ciudades en dos grupos: ciudades de EEUU y ciudades del resto del mundo.

En el segundo CTE, combinamos la información del CTE cities_categorized con daily_sales y vendedor para obtener totales de ventas agrupados por las dos categorías de ciudades y por vendedor.

En el último SELECT, simplemente agrupamos por categoría de ciudades y obtenemos la media de ventas de cada una de las dos categorías (ciudades de EE.UU. y ciudades del resto del mundo).

CTE recursivas

En programación SQL, las CTE recursivas son expresiones comunes de tabla que se referencian a sí mismas. Al igual que las funciones recursivas utilizadas en otros lenguajes de programación, las CTE recursivas se basan en el principio de tomar los datos resultantes de una ejecución anterior, añadirlos o modificarlos y pasar los resultados a la siguiente ejecución. Seguimos haciendo lo mismo hasta que se cumple una condición de parada, que es cuando se obtiene el resultado final.

Los CTEs recursivos deben tener la palabra RECURSIVE después de la palabra WITH. La mejor forma de entender el funcionamiento de las CTEs recursivas es utilizando un ejemplo sencillo, como en el siguiente ejercicio.

Ejercicio 7: Uso de CTEs Recursivas para Generar Secuencias

Ejercicio: Utilizar la recursividad para listar todos los enteros del 1 al 10.

Solución:

WITH RECURSIVE ten_numbers(prev_number) AS (
  SELECT 1
  UNION ALL
  SELECT ten_numbers.prev_number + 1
  FROM ten_numbers
  WHERE prev_number < 10
)

SELECT *
FROM ten_numbers;

Explicación: Esta consulta adopta la notación PostgreSQL de CTEs recursivas, que tiene cuatro partes:

  1. Miembro Ancla: Aquí definimos el punto de inicio de la recursión. Esta parte de la consulta debe poder resolverse de forma autónoma, sin necesidad de utilizar resultados de iteraciones anteriores de la misma CTE.
  2. Miembro recursivo: Esta parte se repite tantas veces como sea necesario, utilizando como base los resultados de la iteración anterior .
  3. Condición de terminación: Esta condición se evalúa después de cada repetición del miembro recursivo; cuando se cumpla, el bucle recursivo terminará. Si esta condición no estuviera presente o diera siempre un resultado verdadero, la recursión continuaría indefinidamente.
  4. Invocación: La principal diferencia entre esta consulta SELECT y otras consultas principales CTE es que esta SELECT actúa como desencadenante del ciclo de ejecuciones recursivas.

En este ejercicio, el miembro ancla simplemente devuelve una fila con el número 1. El miembro recursivo toma la(s) fila(s) de la ejecución anterior y añade (mediante la cláusula UNION) una nueva fila con el valor anterior incrementado en 1. La condición de terminación establece que la consulta continuará iterando hasta que el valor obtenido sea igual a 10.

Ejercicio 8: Utilización de una CTE Recursiva para Recorrer una Estructura de Datos Jerárquica (Árbol)

Para este ejercicio utilizaremos la tabla employee que tiene las columnas id, first_name, last_name, y superior_id. Sus filas contienen los siguientes datos:

idfirst_namelast_namesuperior_id
1MadelineRaynull
2VioletGreen1
3AltonVasquez1
4GeoffreyDelgado1
5AllenGarcia2
6MarianDaniels2


Ejercicio: Mostrar todos los datos de cada empleado, más un texto que muestre la ruta en la jerarquía de la organización que separa a cada empleado del jefe superior (identificado por el valor literal 'Jefe)'.

Solución:

WITH RECURSIVE hierarchy AS (
  SELECT
    id,
    first_name,
    last_name,
    superior_id,
    'Boss' AS path
  FROM employee
  WHERE superior_id IS NULL
  UNION ALL
  SELECT
    employee.id,
    employee.first_name,
    employee.last_name,
    employee.superior_id,
    hierarchy.path || '->' || employee.last_name
  FROM employee JOIN hierarchy
  ON employee.superior_id = hierarchy.id
)

SELECT *
FROM hierarchy;

Explicación: Los datos de la tabla employee representan una estructura jerárquica o de árbol, donde cada fila tiene una columna que la relaciona con su superior (otra fila de la misma tabla). La fila que corresponde al jefe de la empresa (el nodo raíz del árbol) es la que tiene un valor nulo en la columna superior_id. Por lo tanto, ese es nuestro miembro ancla para construir este CTE recursivo. La ruta de este miembro ancla simplemente lleva el valor literal 'Jefe'.

A continuación, el miembro de consulta recursiva une la iteración anterior de la jerarquía con employeeestableciendo la condición de que los jefes de los empleados de la iteración actual (superior_id) ya estén en la jerarquía. Esto significa que, para cada iteración, añadimos otra capa a la jerarquía. Esta capa está formada por los subordinados de los empleados que se añadieron en la iteración anterior. De ahí que la condición de unión sea employee.superior_id = hierarchy.id.

La ruta de cada empleado se ensambla concatenando la ruta de su jefe (hierarchy.path, que muestra todo el camino hasta 'Boss') con el apellido del empleado de la iteración actual, unido por una cadena que representa una flecha (hierarchy.path || '->' || employee.last_name).

Ejercicio 9: Mostrar la trayectoria de un árbol partiendo del nodo raíz

Ejercicio: Muestre una lista que contenga el nombre y apellidos de cada empleado (incluido el jefe superior), junto con un texto (el campo ruta) que muestre la ruta del árbol entre cada empleado y el jefe superior. En el caso del jefe superior, la columna path debe mostrar la dirección last_name del jefe.

Solución:

WITH RECURSIVE hierarchy AS (
  SELECT
	first_name,
	last_name,
	CAST(last_name AS text) AS path
  FROM employee
  WHERE superior_id IS NULL
  UNION ALL
  SELECT
	employee.first_name,
	employee.last_name,
	hierarchy.path || '->' || employee.last_name AS path
  FROM employee, hierarchy
  WHERE employee.superior_id = hierarchy.id
)

SELECT *
FROM hierarchy;

Explicación: La solución de este ejercicio es muy similar a la del ejercicio anterior, con la única salvedad de que el valor path del nodo raíz no es un valor literal de tipo TEXTO; es un valor last_name de la employee tabla. Esto nos obliga a realizar una conversión de datos para evitar obtener un error al ejecutar esta consulta.

Dado que la CTE realiza una UNION entre los datos devueltos por el componente ancla y los datos devueltos por el componente recursivo, es imprescindible que ambos conjuntos de resultados tengan el mismo número de columnas y que los tipos de datos de las columnas coincidan.

La columna last_name de la tabla employee (referida como path en el miembro ancla CTE) es de tipo VARCHAR, mientras que la concatenación hierarchy.path || '->' || employee.last_name (referida como path en el miembro recursivo) produce automáticamente una columna TEXT. Para que UNION no cause un error de desajuste de tipo, es necesario CAST(last_name AS text) en el miembro ancla. De esta forma, las columnas path de ambas partes de la CTE serán TEXT.

Ejercicio 10: Utilización de Múltiples Columnas Recursivas

Ejercicio: Listar todos los datos de cada empleado más el recorrido en la jerarquía hasta llegar al jefe superior, Incluir una columna llamada distancia que muestre el número de personas en la jerarquía desde el jefe superior hasta el empleado. Para el jefe superior, la distancia es 0; para sus subordinados, es 1; para los subordinados de sus subordinados, es 2, y así sucesivamente.

Solución:

WITH RECURSIVE hierarchy AS (
  SELECT
	id,
	first_name,
	last_name,
	superior_id,
	'Boss' AS path,
	0 AS distance
  FROM employee
  WHERE superior_id IS NULL
  UNION ALL
  SELECT
	employee.id,
	employee.first_name,
	employee.last_name,
	employee.superior_id,
	hierarchy.path || '->' || employee.last_name,
	hierarchy.distance + 1
 FROM employee, hierarchy
 WHERE employee.superior_id = hierarchy.id
)
SELECT *
FROM hierarchy;

Explicación: Como hay dos columnas recursivas en el CTE, es necesario indicar un valor inicial para cada una de ellas en el miembro ancla. En este caso, la columna camino tiene el valor inicial 'Jefe' (como en el ejercicio 8) y la columna distancia tiene el valor 0. Luego, en el miembro recursivo, el valor de la distancia resulta de sumar 1 a la distancia de la iteración anterior.

Ejercicio 11: Uso de CTEs Recursivas para Recorrer Estructuras de Datos de Tipo Red

Para este ejercicio, utilizaremos dos tablas: una tabla destination (formada por las columnas id y name) y una tabla ticket (formada por las columnas city_from, city_to, y cost ). La tabla destination contiene los ID y los nombres de un grupo de ciudades, mientras que la tabla ticket tabla indica el coste de los billetes entre los pares de ciudades de la destination (si existen tales conexiones).

Estos son datos de muestra de ambas tablas (primero destinationy después ticket):

idname
1Warsaw
2Berlin
3Bucharest
4Prague
city_fromcity_tocost
12350
1380
14220
23410
24230
32160
34110
42140
4375

Ejercicio: Encuentre la ruta más barata para viajar entre todas las ciudades de la destination partiendo de Varsovia. La consulta debe mostrar las siguientes columnas

  • path - Los nombres de las ciudades del trayecto, separados por '->'.
  • last_id - El id de la ciudad final del trayecto.
  • total_cost - La suma de los costes de los billetes.
  • count_places - El número de ciudades visitadas. Este debe ser igual al número total de ciudades en destinationes decir, 4.

Solución:

WITH RECURSIVE travel(path, last_id,
	total_cost, count_places) AS (
  SELECT
	CAST(name as text),
	Id,
	0,
	1
  FROM destination
  WHERE name = 'Warsaw'
  UNION ALL
  SELECT
	travel.path || '->' || c2.name,
	c2.id,
	travel.total_cost + t.cost,
	travel.count_places + 1
  FROM travel
  JOIN ticket t
	ON travel.last_id = t.city_from
  JOIN destination c1
	ON c1.id = t.city_from
  JOIN destination c2
	ON c2.id = t.city_to
  WHERE position(c2.name IN travel.path) = 0
)
SELECT *
FROM travel
WHERE count_places = 4
ORDER BY total_cost ASC;

Explicación: El método para resolver este ejercicio es similar al del ejercicio anterior. En este caso, sin embargo, no hay un orden directo en la relación entre los elementos de la misma tabla. En su lugar, las relaciones entre los elementos de la destination se expresan en la tabla ticket tabla, vinculando cada par de ciudades conectadas.

La primera fila de los resultados de la consulta anterior muestra la ruta de menor coste. Esto es posible porque la SELECT externa de la consulta ordena los resultados en orden ascendente por total_cost. A su vez, el SELECT externo se asegura de que el número de ciudades recorridas sea 4 estableciendo la condición de que count_places sea igual a 4.

El miembro recursivo se asegura de que cada iteración añada una nueva ciudad a la ruta estableciendo la condición de que el nombre de la ciudad no esté ya en la ruta (position(c2.name IN travel.path) = 0).

Dado que el punto de partida del viaje es la ciudad de Varsovia, el miembro ancla de la ETC recursiva es la fila de la tabla destination donde el nombre es igual a "Varsovia". Observe que convertimos la columna nombre al tipo de datos TEXT (como en el Ejercicio 9) para que el tipo de datos coincida con la columna correspondiente en el miembro CTE recursivo.

¿Necesita más Ejercicios de Expresiones Comunes de Tabla?

Si ha estado trabajando a través de los ejercicios SQL en este artículo, ahora sabe como usar expresiones comunes de tabla . Y sabe lo útiles que pueden ser las CTEs. Estos ejercicios provienen de nuestro curso Consultas recursivas y expresiones de tabla comunes y hay más ejercicios como estos en el curso completo.

Para ser realmente bueno en el análisis de datos con SQL, piense en unirse a LearnSQL.es. Le recomendamos que visite Todo, para siempre SQL Package. Te da acceso de por vida a todos los cursos de SQL que ofrecemos ahora, además de los nuevos que añadamos más adelante. De esta forma, podrás seguir aprendiendo nuevas habilidades para siempre. Únase hoy mismo a LearnSQL.es y empiece a labrarse un gran futuro en el análisis de datos.