Subconsultas en SQL: IN, EXISTS y subconsultas correlacionadas

Actualizado el 29 de septiembre de 2026

Una subconsulta es una consulta dentro de otra, entre paréntesis. Sirve cuando para responder una pregunta primero hay que calcular algo: "los productos más caros que el promedio" necesita conocer el promedio, y "los productos que nunca se vendieron" necesita mirar otra tabla.

Los ejemplos usan la tabla de productos de las guías anteriores y una tabla de ventas, donde producto_id indica qué producto se vendió:

Estructura de productos
CREATE TABLE productos (
  id        INTEGER PRIMARY KEY,
  nombre    TEXT,
  categoria TEXT,
  precio    INTEGER,
  stock     INTEGER
);
productos
idnombrecategoriapreciostock
1TecladoComputación2599012
2MouseComputación1299030
3MonitorComputación1599904
4AudífonosAudio349900
5ParlanteAudio499907
6SillaMuebles899903
7EscritorioMuebles129990NULL
ventas
idproducto_idcantidad
112
225
311
451
531

Una subconsulta que devuelve un valor

SELECT nombre, precio
FROM productos
WHERE precio > (SELECT AVG(precio) FROM productos)
ORDER BY precio DESC;
nombreprecio
Monitor159990
Escritorio129990
Silla89990

La subconsulta calcula el promedio (71.990) y la consulta externa lo usa como si fuera un número escrito a mano. No se puede escribir WHERE precio > AVG(precio) directamente, porque en el WHERE no se permiten agregaciones.

IN: comparar contra una lista de valores

SELECT nombre
FROM productos
WHERE id IN (SELECT producto_id FROM ventas);
nombre
Teclado
Mouse
Monitor
Parlante

La subconsulta devuelve una columna con los producto_id que aparecen en ventas, y IN se queda con los productos cuyo id está en esa lista. El teclado aparece una sola vez aunque tenga dos ventas: IN solo pregunta si está, no cuántas veces.

EXISTS y NOT EXISTS

SELECT p.nombre
FROM productos p
WHERE NOT EXISTS (
  SELECT 1 FROM ventas v WHERE v.producto_id = p.id
);
nombre
Audífonos
Silla
Escritorio

EXISTS es verdadero si la subconsulta devuelve al menos una fila, y NOT EXISTS si no devuelve ninguna. Lo que se selecciona adentro da igual (por convención, SELECT 1). Esta subconsulta usa p.id, una columna de la consulta externa: se evalúa una vez por cada producto. Es una subconsulta correlacionada.

Subconsultas correlacionadas

Una subconsulta correlacionada puede calcular algo distinto para cada fila. Por ejemplo, el producto más caro de cada categoría:

SELECT p.categoria, p.nombre, p.precio
FROM productos p
WHERE p.precio = (
  SELECT MAX(p2.precio)
  FROM productos p2
  WHERE p2.categoria = p.categoria
)
ORDER BY p.categoria;
categorianombreprecio
AudioParlante49990
ComputaciónMonitor159990
MueblesEscritorio129990

Para cada producto p, la subconsulta busca el precio máximo de su categoría, y el producto se queda si su precio coincide. Los alias p y p2 son necesarios porque es la misma tabla dos veces. Si dos productos empataran en el máximo, aparecerían ambos.

Subconsultas en el FROM

El resultado de una consulta es una tabla, así que se puede usar en el FROM como si fuera una. Es útil para filtrar o volver a agrupar un resultado ya agrupado:

SELECT categoria, total_unidades
FROM (
  SELECT p.categoria, SUM(v.cantidad) AS total_unidades
  FROM ventas v
  JOIN productos p ON p.id = v.producto_id
  GROUP BY p.categoria
) AS resumen
WHERE total_unidades >= 2;
categoriatotal_unidades
Computación9

La subconsulta calcula las unidades vendidas por categoría (Computación 9, Audio 1) y la consulta externa filtra ese resultado. Aquí daría lo mismo usar HAVING, pero cuando hay que combinar varios resúmenes, una subconsulta en el FROM (o un WITH, que es lo mismo con nombre) ordena mucho el razonamiento. Dale siempre un alias: en varias bases de datos es obligatorio.

Errores comunes

NOT IN con valores NULL

WHERE id NOT IN (SELECT producto_id FROM ventas) funciona con estos datos, pero si alguna venta tuviera producto_id en NULL, la consulta devolvería cero filas: comparar con un NULL da un resultado desconocido, y NOT IN necesita que todas las comparaciones sean falsas. NOT EXISTS no tiene este problema, y por eso es la opción más segura para buscar lo que falta.

Usar = con una subconsulta que devuelve varias filas

WHERE id = (SELECT producto_id FROM ventas) falla en PostgreSQL ("more than one row returned by a subquery"). SQLite no da error: usa la primera fila y devuelve solo el teclado, un resultado que parece válido y no lo es. Si la subconsulta puede devolver varias filas, usa IN.

Subconsultas correlacionadas sobre tablas grandes

Una subconsulta correlacionada se evalúa, en principio, una vez por fila de la consulta externa. Con tablas grandes puede ser lenta. Muchas veces se puede reescribir con un JOIN y GROUP BY; conviene conocer ambas formas.

Ejercicios resueltos

Intenta resolver cada ejercicio antes de abrir la solución: equivocarse y corregir es la parte que más enseña.

1.Productos más caros que el promedio

Escribe una consulta que muestre nombre y precio de los productos cuyo precio supera el promedio de todos los productos.

Ver solución
Solución
SELECT nombre, precio
FROM productos
WHERE precio > (SELECT AVG(precio) FROM productos);

El resultado son el monitor, el escritorio y la silla (sin el ORDER BY, en el orden que devuelva la base). La subconsulta devuelve un solo valor, así que se puede comparar con >. Si el promedio cambia porque se agregan productos, la consulta sigue siendo correcta: por eso es mejor que escribir 71990 a mano.

2.Productos que nunca se vendieron

Escribe una consulta que devuelva el nombre y el stock de los productos que no tienen ninguna venta.

Ver solución
Solución
SELECT p.nombre, p.stock
FROM productos p
WHERE NOT EXISTS (
  SELECT 1 FROM ventas v WHERE v.producto_id = p.id
);
nombrestock
Audífonos0
Silla3
EscritorioNULL

NOT EXISTS es la forma más segura de buscar lo que falta. El mismo resultado se obtiene con un LEFT JOIN y WHERE v.id IS NULL, como en la guía de JOINs. Con NOT IN también funcionaría con estos datos, pero se rompe si aparece un NULL en la columna.

3.El producto más barato de cada categoría

Escribe una consulta que muestre, para cada categoría, el nombre y el precio de su producto más barato.

Ver solución
Solución
SELECT p.categoria, p.nombre, p.precio
FROM productos p
WHERE p.precio = (
  SELECT MIN(p2.precio)
  FROM productos p2
  WHERE p2.categoria = p.categoria
)
ORDER BY p.categoria;
categorianombreprecio
AudioAudífonos34990
ComputaciónMouse12990
MueblesSilla89990

Es la subconsulta correlacionada de la guía con MIN en vez de MAX. La condición p2.categoria = p.categoria es lo que hace que el mínimo se calcule por categoría: sin ella, se compararía contra el mínimo de toda la tabla y solo aparecería el mouse.

Practica SQL con ejercicios a tu nivel

En Coditer resuelves ejercicios en el navegador, con tests automáticos que revisan tu código y una IA que te da pistas cuando te atascas. Practicar es gratis.