7th Jul 2022 Lectura de 9 minutos Cómo usar 2 CTEs en una sola consulta SQL Tihomir Babic SQL aprender SQL CTE Índice 2 CTEs, 1 consulta SQL, 0 problemas ¿Pero puedo usar un CTE dentro de otro CTE? Uso de CTEs Recursivos con CTEs No Recursivos La combinación de dos o más CTEs realmente funciona ¿Te has preguntado alguna vez cómo utilizar varias CTEs en una consulta SQL? Lea este artículo y descubra los CTEs recursivos. Después de aprender las expresiones comunes de tabla o CTEs, una pregunta natural es "¿Puedo usar varias CTEs en una consulta?" Sí, se puede. Y puedes hacerlo con bastante facilidad, especialmente si ya tienes algunos conocimientos básicos sobre las CTEs. Tanto si conoces un poco los CTEs como si eres completamente nuevo en el mundo de los CTEs, leer sobre qué es un CTE es siempre un buen comienzo. Lo que aprenderás en este artículo puede ser practicado en el curso LearnSQL.es Consultas Recursivas, que fue nuestro curso del mes de agosto. Comenzaré explicando cómo utilizar dos CTEs en una consulta. Luego te enseñaré a usar CTEs donde el segundo CTE hace referencia al primero. Para terminar este artículo, te mostraré dos consultas con CTEs, una de las cuales es recursiva. 2 CTEs, 1 consulta SQL, 0 problemas Imaginemos que hay una tabla llamada logins que almacena login datos. Contiene las columnas: id - El ID del inicio de sesión. username - El usuario que inició la sesión. login_date - La fecha de ese inicio de sesión. login_start - Cuando el usuario inició la sesión. login_end - Cuando el usuario se desconectó. Si observas la tabla, verás que todos los nombres de usuario aparecen al menos una vez. idusernamelogin_datelogin_startlogin_end 1JohnXYZ2020-07-037:02:547:08:12 2JohnXYZ2020-07-059:03:2111:08:04 3JohnXYZ2020-07-1214:08:1214:52:13 4Sarah822020-07-0814:05:1215:01:56 5Sarah822020-07-0816:22:4717:13:00 6SugarCane1232020-07-0218:22:4718:42:15 7SugarCane1232020-07-2510:12:5312:52:44 Si tu tarea es calcular el tiempo medio (en minutos) que ha pasado cada usuario conectado, ¿cómo lo harías utilizando CTEs? Necesitarás dos CTEs, con el siguiente código: WITH distinct_user AS ( SELECT DISTINCT username FROM logins), minutes_logged AS ( SELECT username, DATEDIFF (minute, login_start, login_end) AS minutes FROM logins) SELECT u.username, AVG (minutes) AS avg_time_logged FROM distinct_user AS u JOIN minutes_logged AS m ON u.username = m.username GROUP BY u.username; Primero, mencionaré que tienes que tener nombres de usuario distintos para que esto funcione. Ahora, comencemos a escribir el primer CTE. Nada inusual aquí - como con cada CTE, lo defines por WITH; ¡no se llaman WITH queries por nada! Lo que sigue es el nombre del CTE; en este caso, el primer CTE se llama distinct_user. Le sigue la palabra clave AS, y luego simplemente define la sentencia regular SELECT dentro de los paréntesis. Esta sentencia SELECT simplemente selecciona los nombres de usuario únicos de la tabla logins. Una vez que tengas los distintos nombres de usuario, tienes que calcular el tiempo que el usuario estuvo conectado. Esta es la parte crítica: escribir el segundo CTE. Mi segundo CTE se llama minutes_logged. Notarás que no hay WITH antes del segundo CTE. Esto es muy importante. Después de definir el primer CTE, está separado del segundo sólo por la coma, es decir, se escribe WITH sólo una vez. Después, no importa cuántos CTEs definas; sólo es importante que los separes con una coma y que empieces cada CTE con su nombre. Analicemos ahora lo que hace el CTE minutes_logged. Primero toma la columna nombre de usuario de la tabla logins. Luego calcula la diferencia entre login_start y login_end en minutos. Este nuevo valor se mostrará en la columna minutes. Tendrás que calcular el tiempo medio (en minutos) que cada usuario ha estado conectado. Para esa parte del cálculo, he utilizado la consulta externa. Selecciona el nombre de usuario del CTE distinct_user y luego calcula el promedio de minutos de conexión utilizando la columna minutes del CTE minutes_logged. Estos dos CTEs se unen utilizando la columna nombre de usuario. Finalmente, el resultado se agrupa por la misma columna, ya que queremos el resultado a nivel de usuario. usernameavg_time_logged JohnXYZ58 Sarah8253 SugarCane12390 ¿Pero puedo usar un CTE dentro de otro CTE? Una pregunta sencilla merece una respuesta sencilla: sí, se puede. Ahora que sabe cómo utilizar múltiples CTEs, escribir un CTE que haga referencia a otro CTE es sólo una variación de lo que ha aprendido. Como es una variación, creo que lo mejor es mostrarte cómo hacerlo utilizando un ejemplo con el que ya estás familiarizado. Volvamos al ejemplo anterior. Modificaré un poco el código para mostrarte cómo escribir un CTE que haga referencia al primer CTE. Esta es la nueva versión de nuestro código anterior: WITH difference AS ( SELECT username, DATEDIFF (minute, login_start, login_end) AS minutes FROM logins), average_logged AS ( SELECT username, AVG (minutes) AS average FROM difference GROUP BY username) SELECT DISTINCT username, average FROM average_logged; La lógica es la misma que en el primer ejemplo, sólo que los pasos están en un orden different. El primer CTE, diferencia, calcula los minutos que se han gastado con cada inicio de sesión; esto es lo mismo que en el primer ejemplo. Ahora que tengo los minutos, tengo que calcular el tiempo medio (en minutos) empleado por cada usuario. Para eso usaré el promedio del CTE. Como recuerdas, ponemos una coma entre los dos CTEs y empezamos el segundo por su nombre - ¡no hace falta volver a usar WITH! Este segundo CTE calcula el tiempo medio de cada inicio de sesión; para ello, utiliza la columna minutes del primer CTE y almacena el resultado en la columna average. Para referenciar el CTE difference en el segundo CTE, lo trata como una tabla: FROM difference. Por último, se agrupa el resultado por el nombre de usuario, ya que no se desea obtener la media de todos los usuarios. La consulta externa simplemente selecciona las columnas username y el promedio del CTE average_logged. De este modo, se obtiene el mismo resultado que en el primer ejemplo: usernameavg_time_logged JohnXYZ58 Sarah8253 SugarCane12390 Si estos ejemplos no son suficientes para ti, hay un artículo que te da varios ejemplos más para practicar. Esto incluye el uso de más de un CTE también. Uso de CTEs Recursivos con CTEs No Recursivos Hay una razón por la que los CTEs se enseñan en nuestro Consultas recursivas y expresiones de tabla comunes curso. Es porque los CTEs también pueden ser recursivos. Esta es la parte más complicada de aprender CTEs. (Asumo que ya sabes qué es una CTE recursiva y cómo escribirla. Si no es así, vea Consulta SQL Larga vs. Consulta SQL Recursiva para una introducción y Hazlo en SQL: Recorrido de Árbol SQL Recursivo para un ejemplo en profundidad. ) Sin embargo, tal vez no te hayas dado cuenta de que puedes utilizar más de un CTE, incluso si uno de ellos es recursivo. Veamos cómo se hace. Para este ejemplo, imaginemos que quieres comprar una empresa con tus amigos. Hay cuatro opciones para la inversión, cada una de las cuales requiere una cantidad diferente de dinero. Todavía estás en las primeras conversaciones. No estás seguro de cuántos amigos participarán; su participación depende de la cantidad de dinero que tengan para invertir. Esta cantidad depende del número total de inversores que participen y de la opción de inversión elegida. Para ayudarles a decidir, has decidido calcular la cantidad necesaria por inversor para las cuatro opciones de inversión con un número de inversores de uno a diez. En este ejemplo, tenemos primero una ETC no recursiva seguida de una ETC recursiva: WITH RECURSIVE investment AS ( SELECT amount 5897645 AS investment_amount UNION SELECT 4536841 AS investment_amount UNION SELECT 3852457 AS investment_amount UNION SELECT 3452115 AS investment_amount ), per_investor AS ( SELECT 0 AS investors_number, 0 AS amount, 0 AS individual_amount UNION SELECT investors_number + 1, investment_amount, investment_amount/(investors_number + 1) FROM investment, per_investor WHERE investors_number < 10) SELECT * FROM per_investor ORDER BY amount, investors_number; La principal distinción es evidente desde el principio. Al escribir consultas recursivas, tienes que empezar tu código usando WITH RECURSIVE. Como notarás, no tienes que empezar con el CTE recursivo en sí; en este ejemplo, un CTE no recursivo viene primero, aunque usamos WITH RECURSIVE inmediatamente antes de definirlo. Puedes escribir las CTEs en el orden que quieras; la recursiva puede ser la primera o la última. Pero es importante recordar que si vas a tener al menos una consulta recursiva, escribir WITH RECURSIVE es obligatorio. En el ejemplo actual, mi primer CTE (no recursivo) se llama inversión. Como no tengo la tabla con todos los importes de inversión, he decidido utilizar el CTE como tabla temporal. Como puede ver, los posibles importes de inversión son los siguientes 5,897,645 4,536,841 3,852,457 3,452,115 Al utilizar UNION con SELECT en el primer CTE, estoy creando prácticamente una tabla que contiene esas cuatro posibilidades de inversión. Se mostrarán en la columna investment_amount. La tabla resultante para este CTE es: investment_amount 3,452,115 3,852,457 4,536,841 5,897,645 El segundo CTE es recursivo. Sin embargo, esto no cambia nada en comparación con la escritura de dos CTEs no recursivas: la coma vuelve a separar las CTEs. Analicemos un poco el segundo CTE. La primera sentencia SELECT define tres columnas que tienen el valor 0: investors_number, cantidad y individual_amount. Como he dicho, el CTE recursivo se refiere a sí mismo, es decir, se requiere el operador UNION. El operador UNION va seguido de una o más sentencias SELECT, que realizará la operación deseada en la primera sentencia SELECT. Esto significa que añadirá 1 a la columna investors_number. Luego pondrá investment_amount de la primera sentencia CTE en la columna cantidad. Dividirá el importe de la inversión con el número de inversores implicados; el resultado se muestra en la columna individual_amount. Realizará esta operación para los cuatro importes de inversión hasta llegar a diez inversores. La consulta externa simplemente selecciona todos los datos del CTE per_investor, con los datos ordenados por las columnas importe y investors_number. Ejecute esta consulta y disfrute de todas las posibilidades que ha calculado: investors_numberamountindividual_amount 000 13,452,1153,452,115 23,452,1151,726,057 33,452,1151,150,705 43,452,115863,028 53,452,115690,423 63,452,115575,352 73,452,115493,159 83,452,115431,514 93,452,115383,568 103,452,115345,211 13,852,4573,852,457 23,852,4571,926,228 33,852,4571,284,152 43,852,457963,114 53,852,457770,491 63,852,457642,076 73,852,457550,351 83,852,457481,557 93,852,457428,050 103,852,457385,245 14,536,8414,536,841 24,536,8412,268,420 34,536,8411,512,280 44,536,8411,134,210 54,536,841907,368 64,536,841756,140 74,536,841648,120 84,536,841567,105 94,536,841504,093 104,536,841453,684 15,897,6455,897,645 25,897,6452,948,822 35,897,6451,965,881 45,897,6451,474,411 55,897,6451,179,529 65,897,645982,940 75,897,645842,520 85,897,645737,205 95,897,645655,293 105,897,645589,764 Impresionante, ¿verdad? La combinación de dos o más CTEs realmente funciona Las expresiones comunes de tabla de SQL son una herramienta poderosa. Son útiles si quieres que tu código sea más legible, pero su valor no es sólo cosmético; sus posibilidades reales brillan si sabes cómo usar varias CTEs en una consulta o incluso escribir una CTE recursiva en SQL. Como has visto, no es difícil combinar dos o más CTEs. Una vez que sepas eso, realmente se abren las posibilidades de usar CTEs en varias situaciones. Ahora depende de ti encontrar los casos en los que tendrás que usar CTEs. ¡Y practica, practica, practica! Para ello, siempre es recomendable utilizar nuestro curso Consultas recursivas y expresiones de tabla comunes . Te dará tablas para practicar, así que no tendrás que preocuparte de crear escenarios tú mismo. No dudes en compartir tu experiencia con los CTEs de SQL en nuestra sección de comentarios. Tags: SQL aprender SQL CTE