SQL Subconsultas (Subqueries): Guía Completa con Ejemplos
¿Qué es una Subconsulta?
Una subconsulta es una consulta SQL anidada dentro de otra consulta. También se conoce como "subquery" o "consulta interna". Es una técnica poderosa para resolver problemas complejos.
Tipos de Subconsultas
1. Subconsulta Escalar (Retorna un valor)
SELECT
nombre,
salario,
(SELECT AVG(salario) FROM empleados) as salario_promedio
FROM empleados;
2. Subconsulta de Fila (Retorna una fila)
SELECT *
FROM empleados
WHERE (departamento_id, salario) = (
SELECT departamento_id, MAX(salario)
FROM empleados
GROUP BY departamento_id
LIMIT 1
);
3. Subconsulta de Tabla (Retorna múltiples filas)
SELECT *
FROM empleados
WHERE departamento_id IN (
SELECT id FROM departamentos WHERE activo = 1
);
---
Ubicaciones de Subconsultas
En SELECT
SELECT
p.nombre,
p.precio,
(SELECT AVG(precio) FROM productos WHERE categoria_id = p.categoria_id) as precio_promedio_categoria
FROM productos p;
En FROM (Derived Table)
SELECT departamento, promedio_salario
FROM (
SELECT
d.nombre as departamento,
AVG(e.salario) as promedio_salario
FROM empleados e
JOIN departamentos d ON e.departamento_id = d.id
GROUP BY d.id, d.nombre
) as resumen
WHERE promedio_salario > 50000;
En WHERE
SELECT *
FROM productos
WHERE precio > (SELECT AVG(precio) FROM productos);
En HAVING
SELECT categoria_id, COUNT(*) as total
FROM productos
GROUP BY categoria_id
HAVING COUNT(*) > (
SELECT AVG(cantidad)
FROM (
SELECT COUNT(*) as cantidad
FROM productos
GROUP BY categoria_id
) as conteo
);
---
Operadores con Subconsultas
IN / NOT IN
-- Clientes que han hecho compras
SELECT * FROM clientes
WHERE id IN (SELECT DISTINCT cliente_id FROM pedidos);
EXISTS / NOT EXISTS
-- Más eficiente que IN para tablas grandes
SELECT * FROM clientes c
WHERE EXISTS (
SELECT 1 FROM pedidos p WHERE p.cliente_id = c.id
);
ANY / SOME
-- Empleados que ganan más que algún gerente
SELECT * FROM empleados
WHERE salario > ANY (
SELECT salario FROM empleados WHERE puesto = 'Gerente'
);
ALL
-- Empleados que ganan más que todos los gerentes
SELECT * FROM empleados
WHERE salario > ALL (
SELECT salario FROM empleados WHERE puesto = 'Gerente'
);
---
Subconsultas Correlacionadas
Una subconsulta correlacionada hace referencia a la consulta externa:
-- Empleados que ganan más que el promedio de su departamento
SELECT e.nombre, e.salario, e.departamento_id
FROM empleados e
WHERE e.salario > (
SELECT AVG(salario)
FROM empleados
WHERE departamento_id = e.departamento_id
);
Ejemplo: Top N por Grupo
-- Los 3 productos más caros de cada categoría
SELECT *
FROM productos p1
WHERE (
SELECT COUNT(*)
FROM productos p2
WHERE p2.categoria_id = p1.categoria_id
AND p2.precio > p1.precio
) <
---
Casos de Uso Prácticos
1. Encontrar Duplicados
SELECT *
FROM clientes c
WHERE (
SELECT COUNT(*)
FROM clientes
WHERE email = c.email
) > 1;
2. Segunda Mejor Opción
-- Segundo producto más caro
SELECT *
FROM productos
WHERE precio = (
SELECT MAX(precio)
FROM productos
WHERE precio < (SELECT MAX(precio) FROM productos)
);
3. Comparación con Período Anterior
SELECT
mes,
ventas,
(SELECT ventas FROM ventas_mensuales WHERE mes = DATE_SUB(vm.mes, INTERVAL 1 MONTH)) as ventas_mes_anterior
FROM ventas_mensuales vm;
4. Productos Sin Ventas
SELECT *
FROM productos p
WHERE NOT EXISTS (
SELECT 1
FROM detalle_pedidos dp
WHERE dp.producto_id = p.id
);
---
Subconsultas vs JOINs
A menudo, las subconsultas pueden reescribirse como JOINs:
Con Subconsulta
SELECT *
FROM empleados
WHERE departamento_id IN (
SELECT id FROM departamentos WHERE ubicacion = 'Madrid'
);
Con JOIN
SELECT e.*
FROM empleados e
JOIN departamentos d ON e.departamento_id = d.id
WHERE d.ubicacion = 'Madrid';
¿Cuándo Usar Cada Una?
| Usa Subconsulta | Usa JOIN |
|-----------------|----------|
| Verificar existencia (EXISTS) | Necesitas datos de ambas tablas |
| Comparar con agregados | Múltiples relaciones |
| Lógica más clara | Mejor performance en muchos casos |
| Consultas de eliminación/actualización | Consultas complejas con varios filtros |
---
CTEs: Alternativa Moderna
Common Table Expressions (WITH) hacen las consultas más legibles:
WITH ventas_por_vendedor AS (
SELECT
vendedor_id,
SUM(total) as total_ventas
FROM pedidos
GROUP BY vendedor_id
),
promedio_ventas AS (
SELECT AVG(total_ventas) as promedio
FROM ventas_por_vendedor
)
SELECT v.vendedor_id, v.total_ventas
FROM ventas_por_vendedor v, promedio_ventas p
WHERE v.total_ventas > p.promedio;
---
Errores Comunes
Error 1: Subconsulta Retorna Múltiples Filas
-- Error: más de un valor retornado
SELECT * FROM empleados
WHERE salario = (SELECT salario FROM empleados WHERE departamento_id = 1);
Error 2: NULL con NOT IN
-- Problema: si hay NULLs, NOT IN puede no retornar resultados
SELECT * FROM clientes
WHERE id NOT IN (SELECT cliente_id FROM pedidos); -- puede fallar si hay NULL
Error 3: Performance en Subconsultas Correlacionadas
Las subconsultas correlacionadas se ejecutan una vez por cada fila. Considera reescribir como JOIN.
---
Optimización
1. Usa EXISTS en lugar de IN para tablas grandes 2. Materializa subconsultas con CTEs cuando se usan múltiples veces 3. Revisa el plan de ejecución con EXPLAIN 4. Considera índices en las columnas de la subconsulta
Conclusión
Las subconsultas son herramientas esenciales para resolver problemas complejos en SQL. Dominarlas te permite escribir consultas más expresivas y poderosas.
---
Consejo: Usa AI2sql para generar subconsultas complejas automáticamente y aprende de las diferentes técnicas utilizadas.