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

5 Ejemplos Prácticos de SQL CTE

Las expresiones comunes de tabla (CTEs) se introdujeron en SQL para mejorar la legibilidad y la estructura de las consultas SQL, especialmente aquellas que requieren múltiples pasos para obtener la salida necesaria. En este artículo, repasaremos varios ejemplos para mostrar cómo las CTE de SQL pueden ayudarle con cálculos complejos y estructuras de datos jerárquicas.

Expresiones Comunes de Tabla en SQL

Las expresiones comunes de tabla (CTEs), también llamadas cláusulas WITH, permiten crear subconsultas con nombre que son referenciadas en la consulta principal. Las CTE se introdujeron en SQL para mejorar la legibilidad y la estructura de una sentencia SQL.

La sintaxis básica de las CTE es la siguiente:

WITH subquery_name AS
(SELECT … subquery ...)
SELECT … main query ...

Empezamos con la palabra clave WITH seguida del nombre que asignamos a la CTE (subconsulta). A continuación, ponemos la palabra clave AS e incluimos la subconsulta entre paréntesis. Una vez definida la CTE, pasamos a la consulta principal, donde podemos hacer referencia a esta CTE por su nombre.

Si no conoce las CTEs, puede que necesite consultar este artículo que explica con más detalle cómo funcionan las CTEs.

Es posible tener múltiples CTEs en una consulta, referenciar un CTE dentro de otro (es decir, CTEs anidados), o incluso referenciar un CTE dentro de sí mismo (CTEs recursivos). Esto nos da un montón de herramientas y oportunidades.

Ejemplos de CTE SQL

Para mostrar cómo las CTEs pueden ayudarle con varias tareas analíticas, voy a repasar cinco ejemplos prácticos.

Empezaremos con la tabla orderscon información básica como la fecha del pedido, el ID del cliente, el nombre de la tienda, el ID del empleado que registró el pedido y el importe total del pedido.

orders
iddatecustomer_idstoreemployee_idamount
1012021-07-01234East11198.00
1022021-07-01675West13799.00
1032021-07-01456West14698.00
1042021-07-01980Center1599.00
1052021-07-02594Center161045.45
1062021-07-02435East11599.00
1072021-07-02246West14678.89
1082021-07-03256East12458.80
1092021-07-03785East1299.00
1102021-07-03443Center16325.50

Ahora vamos a escribir un par de consultas SQL. También puedes practicar CTEs SQL en este curso interactivo Consultas recursivas y expresiones de tabla comunes interactivo que cubre todos los tipos de CTEs.

Ejemplo 1

En nuestro primer ejemplo, queremos comparar el importe total de cada pedido con el importe medio de los pedidos en la tienda correspondiente.

Podemos empezar calculando el importe medio de los pedidos de cada tienda utilizando una CTE y añadiendo esta columna a la salida de la consulta principal:

WITH avg_per_store AS
  (SELECT store, AVG(amount) AS average_order
   FROM orders
   GROUP BY store)
SELECT o.id, o.store, o.amount, avg.average_order AS avg_for_store
FROM orders o
JOIN avg_per_store avg
ON o.store = avg.store;

Como puede ver, nuestra consulta comienza con un CTE llamado avg_per_store. Usando este CTE, creamos una tabla que lista todas las tiendas y el importe medio de los pedidos por tienda. Luego, en la consulta principal, seleccionamos para mostrar el ID del pedido, el nombre de la tienda, el importe del pedido de la tabla original orders y el importe medio de los pedidos de cada tienda (avg_for_store) de la CTE definida anteriormente.

Este es el resultado:

idstoreamountavg_for_store
101East198.00338.70
102West799.00725.30
103West698.00725.30
104Center99.00489.98
105Center1045.45489.98
106East599.00338.70
107West678.89725.30
108East458.80338.70
109East99.00338.70
110Center325.50489.98

Con esta tabla, podemos ver cómo se compara cada pedido con el importe medio de los pedidos en la tienda correspondiente.

Pasemos ahora a un ejemplo más complejo.

Ejemplo 2

Aquí compararemos diferentes tiendas. En concreto, queremos ver cómo se compara el importe medio de los pedidos de cada tienda con el mínimo y el máximo del importe medio de los pedidos de todas las tiendas.

Como en el primer ejemplo, empezaremos calculando el importe medio de los pedidos de cada tienda mediante una CTE. A continuación, definiremos otros dos CTEs:

  • Para calcular el mínimo del importe medio de los pedidos entre todas las tiendas.
  • Para calcular el máximo del importe medio de los pedidos de todas las tiendas.

Observe que estos dos CTEs utilizarán el resultado del primer CTE.

Finalmente, en la consulta principal, uniremos los tres CTEs para obtener la información que necesitamos:

WITH avg_per_store AS (
    SELECT store, AVG(amount) AS average_order
    FROM orders
    GROUP BY store),
    min_order_store AS (
    SELECT MIN (average_order) AS min_avg_order_store
    FROM avg_per_store),
    max_order_store AS (
    SELECT MAX (average_order) AS max_avg_order_store
    FROM avg_per_store)
SELECT avg.store, avg.average_order, min.min_avg_order_store,
max.max_avg_order_store
FROM avg_per_store avg
CROSS JOIN min_order_store min
CROSS JOIN max_order_store max;

Como puede ver, incluso con múltiples CTEs anidados, la consulta SQL permanece limpia y fácil de seguir. Si utilizáramos subconsultas, tendríamos que anidar una subconsulta dentro de las otras dos y repetirla varias veces dentro de la misma consulta. Aquí, con las CTEs, simplemente definimos las tres CTEs al principio y luego las referenciamos cuando sea necesario.

Este es el resultado de la consulta:

storeaverage_ordermin_avg_order_storemax_avg_order_store
Center489.98338.70725.30
East338.70338.70725.30
West725.30338.70725.30

Puede ver fácilmente cómo se compara cada tienda con las demás en cuanto al importe medio de los pedidos. Por supuesto, cuando sólo tiene tres tiendas, podríamos compararlas sin añadir las columnas min_avg_order_store y max_avg_order_store. Sin embargo, cuando se necesita analizar el rendimiento de muchas tiendas por diferentes métricas, este enfoque puede ser muy útil.

Lea esta guía para aprender las mejores prácticas de SQL CTE.

Ejemplo 3

En nuestro siguiente ejemplo, continuaremos comparando el rendimiento de nuestras tiendas pero con algunas métricas diferentes. Digamos que nuestra empresa considera que los pedidos inferiores a 200 $ son pequeños y que los pedidos iguales o superiores a 200 $ son grandes. Ahora, queremos calcular cuántos pedidos grandes y pequeños tuvo cada tienda.

Para abordar esta tarea utilizando las cláusulas WITH, necesitamos dos expresiones de tabla comunes:

  • Obtener el número de pedidos grandes de cada tienda.
  • Obtener el número de pedidos pequeños de cada tienda.

Algunas tiendas pueden no tener ningún pedido grande o ningún pedido pequeño, lo que lleva a los valores de NULL. Tenemos que asegurarnos de no perder ninguna tienda durante los JOINs. Por esta razón, prefiero tener otro CTE que simplemente genere una lista de todas las tiendas. Luego, en la consulta principal, uniremos este CTE con los dos CTE que contienen las métricas de pedidos grandes y pequeños:

WITH stores AS
   (SELECT store
    FROM orders
    GROUP BY store),
  big AS
  (SELECT store, COUNT(*) AS big_orders
   FROM orders
   WHERE amount >= 200.00
   GROUP BY store),
  small AS
  (SELECT store, COUNT(*) AS small_orders
   FROM orders
   WHERE amount < 200.00
   GROUP BY store)
SELECT s.store, b.big_orders, sm.small_orders
FROM stores s
FULL JOIN big b
ON s.store = b.store
FULL JOIN small sm
ON s.store = sm.store;

Así, en esta consulta

  • Definimos el CTE stores para obtener una lista completa de tiendas.
  • Definimos el CTE big para calcular, para cada tienda, el número de pedidos con un importe total igual o superior a 200 $.
  • Definir el CTE small para calcular, para cada tienda, el número de pedidos inferiores a 200 $.
  • Una los tres CTEs.

Este es el resultado:

storebig_orderssmall_orders
Center21
East22
West3NULL

Ahora podemos ver que la tienda del Oeste funciona realmente bien; todos sus pedidos son superiores a 200 $. La tienda del Centro también va bien, con dos pedidos por encima de 200 $ y un pedido por debajo de 200 $. Sólo la mitad de los pedidos de la tienda Este son grandes, con dos pedidos por encima de 200 $ y dos pedidos por debajo de 200 $.

Ejemplo 4

Para los dos siguientes ejemplos, utilizaremos la siguiente tabla con información básica sobre los empleados de nuestra empresa. Específicamente, tenemos el ID del empleado, el nombre, el apellido, el ID del superior del empleado, el departamento y el monto del último bono.

employees
idfirst_namelast_namesuperior_iddepartmentbonus
1JohnDaviesNULLCEO2545.00
2MarkTaylor1Finance1100.00
3KateWilson1Operations900.00
4OliviaWatson3Operations450.00
5JamesAddington1Sales1900.00
6RachaelWhite1Marketing1250.00
7SaraClinton6Marketing1000.00
11JohnSmith5Sales800.00
12NoahJones11Sales500.00
13StevenBrown5Sales900.00
14LiamWilliams13Sales700.00
15PaulLee5Sales500.00
16PatrickEvans15Sales500.00

Ahora, calculemos la bonificación media por departamento y, a continuación, contemos cuántos empleados tuvieron bonificaciones superiores a la media de sus respectivos departamentos y cuántos tuvieron inferiores.

Las expresiones comunes de tabla pueden ser muy útiles con cálculos tan complejos. Tendremos tres CTEs en esta consulta SQL:

  • Para calcular el importe medio de las bonificaciones de cada departamento.
  • Para calcular, por departamento, el número de empleados cuyas bonificaciones estaban por encima de la media de su respectivo departamento.
  • Para calcular, por departamento, el número de empleados cuyas bonificaciones fueron inferiores a la media de sus respectivos departamentos.

En la consulta principal, uniremos los tres CTE.

WITH avg_bonus_department AS
    (SELECT department, AVG(bonus) AS average_bonus
    FROM employees
    GROUP BY department),
    above_average AS
    (SELECT e.department, count(*) AS employees_above_average
     FROM employees e
     JOIN avg_bonus_department avg
     ON e.department = avg.department
     WHERE bonus > average_bonus
     GROUP BY e.department),
     below_average AS
     (SELECT e.department, count(*) AS employees_below_average
     FROM employees e
     JOIN avg_bonus_department avg
     ON e.department = avg.department
     WHERE bonus < average_bonus
     GROUP BY e.department)
SELECT avg.department, avg.average_bonus, aa.employees_above_average, ba.employees_below_average
FROM avg_bonus_department avg
LEFT JOIN above_average aa
ON avg.department = aa.department
LEFT JOIN below_average ba
ON avg.department = ba.department;

He aquí el resultado de la consulta:

departmentaverage_bonusemployees_above_averageemployees_below_average
CEO2545.00NULLNULL
Marketing1125.0011
Finance1100.00NULLNULL
Operations675.0011
Sales828.5725

Como sólo hay una persona en Finanzas, la bonificación media del departamento es exactamente igual a la bonificación de esta persona. Como resultado, no tenemos a nadie en el departamento de Finanzas cuya bonificación esté por encima o por debajo de la media (reflejados como valores NULL en el resultado). Lo mismo ocurre con el director general.

Para el departamento de Ventas, podemos ver que la bonificación media fue de 828,57 $, y sólo dos de siete personas tuvieron bonificaciones por encima de la media del departamento.

Le dejamos que interprete los resultados de los departamentos de Marketing y Operaciones del mismo modo, y pasaremos a un ejemplo aún más complejo con una consulta recursiva.

Ejemplo 5

Las expresiones comunes de tabla pueden referenciarse a sí mismas, lo que las convierte en una herramienta perfecta para analizar estructuras jerárquicas. Veámoslo con un ejemplo.

Utilizando la información de la tabla employees y de la tabla orders podemos dibujar la siguiente estructura organizativa de nuestra empresa. El personal de tienda se considera parte del equipo de Ventas. Además, en la orders tabla, podemos ver qué empleados tienen pedidos en qué tiendas, por lo que podemos deducir la tienda a la que pertenece cada vendedor.

Ejemplos de SQL CTE

Ahora, digamos que necesitamos averiguar el nivel de cada empleado en la estructura organizativa (es decir, el nivel 1 es el director general, el nivel 2 es para sus subordinados directos, etc.). Podemos añadir una columna que muestre esto con una consulta recursiva:

WITH RECURSIVE levels AS (
  SELECT
    id,
    first_name,
    last_name,
    superior_id,
    1 AS level
  FROM employees
  WHERE superior_id IS NULL
  UNION ALL
  SELECT
    employees.id,
    employees.first_name,
    employees.last_name,
    employees.superior_id,
    levels.level + 1
  FROM employees, levels
  WHERE employees.superior_id = levels.id
)

SELECT *
FROM levels;

Como ve, el CTE levels de esta consulta se referencia a sí misma. Comienza seleccionando el registro correspondiente al gran jefe, el que no tiene superior (es decir, superior_id IS NULL). Asignamos 1 al nivel de esta persona, luego usamos UNION ALL para añadir otros registros, añadiéndole uno por cada nivel de dirección en la estructura organizativa.

Este es el resultado:

idfirst_namelast_namesuperior_idlevel
1JohnDaviesNULL1
2MarkTaylor12
3KateWilson12
5JamesAddington12
6RachaelWhite12
4OliviaWatson33
7SaraClinton63
11JohnSmith53
13StevenBrown53
15PaulLee53
12NoahJones114
14LiamWilliams134
16PatrickEvans154

El tema de las consultas recursivas es bastante difícil, así que no voy a entrar en más detalles aquí. Pero por favor asegúrate de revisar este artículo que explica las CTEs recursivas con ejemplos, especialmente si trabajas con datos jerárquicos.

Y consulta este artículo para ver más ejemplos de CTEs SQL.

¡Practiquemos las CTEs SQL!

Espero que estos ejemplos te hayan mostrado lo útiles que pueden ser las CTEs para diferentes tareas analíticas. Ayudan a mejorar la legibilidad y la estructura de tus consultas SQL, ayudan con cálculos anidados y complejos, y son útiles para el procesamiento eficiente de datos jerárquicos. Obtenga más información sobre cuándo utilizar las CTE en este artículo.

Si quieres dominar las expresiones comunes de tabla, te recomiendo que empieces con este único Consultas recursivas y expresiones de tabla comunes curso. Incluye 114 ejercicios interactivos que cubren todo tipo de CTEs, incluyendo CTEs simples, CTEs anidadas y CTEs recursivas. Al final del curso, sabrá cómo gestionar consultas SQL con CTEs, cómo y cuándo anidar CTEs, y cómo utilizar CTEs recursivas para moverse por modelos de datos jerárquicos.

Si quieres dominar otras herramientas avanzadas para el análisis de datos con SQL, deberías plantearte seguir el SQL avanzado curso Va más allá de las CTE y también cubre las funciones de ventana y las extensiones GROUP BY en SQL. Y LearnSQL.es ofrece muchas maneras diferentes de practicar estos conceptos avanzados de SQL en línea.

Gracias por leer y ¡feliz aprendizaje!