Subconsultas correlacionadas
En resumen: Una subconsulta correlacionada usa una columna de la consulta exterior, así que se calcula de nuevo para cada fila, mientras que una subconsulta en FROM convierte su resultado en una tabla nueva.
Problema
¿Caro comparado con qué?
De la lección anterior sabes qué productos están por encima del precio promedio de la tienda:
Todo lo de la cocina es «caro», y ningún pan lo es. Pero el pan no se compara con una sartén. La gerencia quiere saber qué productos son caros para su propia categoría: el queso frente a los lácteos, el café frente a las bebidas.
La idea
Subconsulta correlacionada
Una subconsulta puede usar una columna de la fila que la consulta exterior está mirando en ese momento:
p es el producto de fuera, p2 son los productos de la subconsulta. La condición p2.category_id = p.category_id dice «los productos de su categoría». Para el pan blanco la subconsulta calcula el promedio de pan y bollería (1,60 €), para el queso blanco el promedio de los lácteos (4,66 €).
Una subconsulta así se llama correlacionada: depende de la fila exterior y se calcula de nuevo para cada fila. La subconsulta común de la lección anterior da la misma respuesta para todas las filas.
No funcionaría sin los nombres cortos p y p2: ambos son la tabla products, y la base de datos tiene que saber cuál category_id es el de fuera y cuál el de dentro.
La idea
En WHERE
Ahora la pregunta de la gerencia: ¿qué productos están por encima del promedio de su categoría?
Nueve productos, al menos uno de cada categoría: el pan integral está por encima del promedio de pan y bollería, y el detergente en polvo por encima del promedio de limpieza.
Falta la tarjeta de regalo, aunque cuesta 20 €. Su categoría es NULL, p2.category_id = NULL no es verdadero para ninguna fila, el promedio es NULL y la comparación es desconocida. El mismo NULL de la lección de NULL, solo que mejor escondido.
Pruébalo
El último pedido
Muestra para cada cliente su nombre y la fecha de su último pedido (columna last_order). Los clientes sin pedidos también deben estar en la lista.
Solución
SELECT name,
(SELECT MAX(o.ordered_at) FROM orders o WHERE o.customer_id = c.id) AS last_order
FROM customers c;(SELECT MAX(o.ordered_at) FROM orders o WHERE o.customer_id = c.id) como columna. 20 filas. Teodora y Yavor no tienen pedidos, así que el suyo es NULL. Con JOIN y GROUP BY desaparecerían, a no ser que sea un LEFT JOIN.
Más precisamente, para curiosos
En principio una subconsulta correlacionada se ejecuta una vez por cada fila exterior: 20 clientes, 20 veces. Las bases de datos suelen reescribirla en algo más rápido, pero con tablas grandes conviene compararla con JOIN y GROUP BY. Cómo ver lo que hace la base de datos está en el módulo sobre velocidad.
Una subconsulta correlacionada también da el número del último pedido, no solo su fecha. En el módulo sobre resumir viste que SQLite lo permite con MAX y una columna libre, mientras que otras bases de datos no. Esta es la forma que funciona en todas partes:
18 filas, una por cada cliente con pedidos. Si un cliente tiene dos pedidos en su último día, saldrán los dos.
Míralo
El valor promedio de un pedido
Contabilidad pide el valor promedio de un pedido. El primer intento:
Hacen falta dos pasos: primero el valor de cada pedido, luego el promedio de esos valores. El primer paso ya lo sabes hacer:
La idea
Subconsulta en FROM
El resultado de cada consulta es una tabla. Por eso puede ir en FROM, donde escribes el nombre de una tabla:
La consulta interior hace una tabla de 60 filas, una por pedido. La exterior la lee como cualquier otra tabla y calcula el promedio: 32,73 €. Esto se llama subconsulta en FROM (tabla derivada, derived table). El nombre después de AS es el que le das a la tabla nueva.
Más precisamente, para curiosos
SQLite no exige un nombre para una subconsulta en FROM, pero MySQL y PostgreSQL hasta la versión 16 lo exigen sin excepción. Dale siempre un nombre y la consulta funcionará en todas partes.
Pruébalo
Cuántos productos hay en un pedido
El almacén está planeando las cajas y pregunta: ¿cuántos productos distintos tiene un pedido en promedio? Redondea a dos decimales (columna avg_lines). El campo ya cuenta los productos de cada pedido.
Solución
SELECT ROUND(AVG(lines), 2) AS avg_lines
FROM (
SELECT order_id, COUNT(*) AS lines
FROM order_items
GROUP BY order_id
) AS t;El conteo de cada pedido se convierte en una tabla en FROM, y fuera va ROUND(AVG(lines), 2): 2,53 productos. 152 líneas en 60 pedidos.
Ejercicio
Dos tareas
1. Marketing quiere recuperar a los clientes que no han pedido desde el verano.
Muestra los nombres de los clientes cuyo último pedido es anterior al 1 de julio de 2026.
Solución
SELECT name FROM customers c
WHERE (SELECT MAX(o.ordered_at) FROM orders o WHERE o.customer_id = c.id) < '2026-07-01';WHERE (SELECT MAX(o.ordered_at) FROM orders o WHERE o.customer_id = c.id) < '2026-07-01'. Ocho clientes. Teodora y Yavor no están: sin pedidos la última fecha es NULL, y una comparación con NULL es desconocida.
2. ¿Cuánto gasta en promedio un cliente en la tienda?
Calcula el importe promedio que un cliente ha gastado en sus pedidos, sin contar los cancelados, redondeado a dos decimales (columna avg_spent). Cuenta solo los clientes con al menos un pedido.
Solución
SELECT ROUND(AVG(spent), 2) AS avg_spent
FROM (
SELECT o.customer_id, SUM(oi.quantity * oi.unit_price) AS spent
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
WHERE o.status <> 'cancelled'
GROUP BY o.customer_id
) AS t;En FROM va el total de cada cliente (JOIN de orders y order_items, WHERE o.status <> 'cancelled', GROUP BY o.customer_id), y fuera va ROUND(AVG(spent), 2): 94,28 €. El promedio es sobre 18 clientes. Los dos sin pedidos no están en la subconsulta, así que no bajan el promedio.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Funciones de agregación: COUNT, SUM, AVG · GROUP BY: un informe por grupos · JOIN: dos tablas en una consulta · Subconsultas
- Continúa en: Un informe por pasos: WITH · Funciones de ventana · Claves naturales y desnormalización · Bases de datos columnares y series temporales
- Ejercicios sobre el tema: Subconsultas y ventanas