Subconsultas
Clase práctica. Subconsultas escalares, con IN, con EXISTS, correlacionadas, tablas derivadas y CTE (WITH), paso a paso y con resultados exactos.
Antes de empezar
Trabajamos con la basetablas_libro: empleados (4), alumnos (6), cursos (5, uno sin profesor), profesores (4, uno sin cursos), alumnos_cursos (8) y articulos (5). Ejecutá USE tablas_libro;1. Cómo se ejecuta una subconsulta, paso a paso
Una subconsulta es un SELECT escrito entre paréntesis dentro de otra consulta. Ejemplo: empleados que ganan más que el salario promedio.
SELECT NOMBRE, SALARIO
FROM empleados
WHERE SALARIO > (SELECT AVG(SALARIO) FROM empleados);- Se ejecuta la consulta interna:
AVG(SALARIO)de los 4 empleados. - Devuelve un solo valor: (1500 + 1300 + 1400 + 1325,5) / 4 = 1381,375.
- La consulta externa compara cada salario contra ese valor.
- Resultado: Carlos (1500) y José (1400).
Truco para depurar
Ejecutá primero solo la subconsulta (seleccionala en Workbench y presioná Ctrl + Shift + Enter). Si su resultado es correcto, recién después ejecutá la consulta completa.2. Dónde se puede escribir una subconsulta
| Tipo | Qué devuelve | Se usa con |
|---|---|---|
| Escalar | 1 fila × 1 columna | =, >, <, o como columna en el SELECT |
| De lista | Varias filas × 1 columna | IN, NOT IN, ANY, ALL |
| De tabla | Varias filas × varias columnas | En el FROM (tabla derivada) o en un WITH |
Error 1242
Si usás= y la subconsulta devuelve más de una fila, MySQL responde Subquery returns more than 1 row. En ese caso usá IN.3. Subconsultas con IN y NOT IN
-- Alumnos inscriptos en «SQL desde cero»
SELECT NOMBRE, APELLIDOS
FROM alumnos
WHERE ID_ALUMNO IN (
SELECT ac.ID_ALUMNO
FROM alumnos_cursos AS ac
INNER JOIN cursos AS c ON c.ID_CURSO = ac.ID_CURSO
WHERE c.TITULO = 'SQL desde cero'
);
-- Pablo Hernandaz Mata, Sergio Ot Dirmet
-- Alumnos que NO cursan «SQL desde cero»
SELECT NOMBRE
FROM alumnos
WHERE ID_ALUMNO NOT IN (
SELECT ac.ID_ALUMNO
FROM alumnos_cursos AS ac
INNER JOIN cursos AS c ON c.ID_CURSO = ac.ID_CURSO
WHERE c.TITULO = 'SQL desde cero'
);
-- Jeremias, Teresa, Marta, CarmenLa trampa de NOT IN con NULL (probala)
¿Qué profesores no dictan ningún curso? Parece fácil, perocursos.ID_PROFE tiene un NULL («SQL avanzado»):
SELECT NOMBRE FROM profesores
WHERE ID_PROFE NOT IN (SELECT ID_PROFE FROM cursos);
-- 0 filas, ¡sin ningún aviso!NOT IN descarta todas las filas. Soluciones: filtrar los NULL dentro de la subconsulta (WHERE ID_PROFE IS NOT NULL) o usar NOT EXISTS. Las dos devuelven Carlos.4. EXISTS y NOT EXISTS
EXISTS solo pregunta «¿hay al menos una fila?». No importa qué columnas devuelva la subconsulta; por convención se escribe SELECT 1.
-- Cursos con al menos un alumno inscripto
SELECT c.TITULO
FROM cursos AS c
WHERE EXISTS (SELECT 1 FROM alumnos_cursos AS ac WHERE ac.ID_CURSO = c.ID_CURSO);
-- Programación PHP, Modelos abstracto de datos, SQL desde cero, Dibujo técnico
-- Profesores sin cursos: la forma segura (no le afectan los NULL)
SELECT p.NOMBRE
FROM profesores AS p
WHERE NOT EXISTS (SELECT 1 FROM cursos AS c WHERE c.ID_PROFE = p.ID_PROFE);
-- Carlos5. Subconsultas correlacionadas
Una subconsulta es correlacionada cuando usa una columna de la consulta externa (e.CARGO). No se ejecuta una sola vez: se recalcula para cada fila.
-- Empleados que ganan más que el promedio de SU cargo
SELECT e.NOMBRE, e.CARGO, e.SALARIO
FROM empleados AS e
WHERE e.SALARIO > (
SELECT AVG(e2.SALARIO)
FROM empleados AS e2
WHERE e2.CARGO = e.CARGO
);Escalar en el SELECT: una columna calculada por cada fila.
SELECT p.NOMBRE,
(SELECT COUNT(*) FROM cursos AS c
WHERE c.ID_PROFE = p.ID_PROFE) AS cursos
FROM profesores AS p;
-- Federico 1 · Ana 1 · Rosa 2 · Carlos 06. Tablas derivadas (subconsulta en el FROM)
Primero se arma una tabla temporal con la subconsulta y después se consulta sobre ella. Siempre necesita un alias; si falta, MySQL responde con el error 1248 (Every derived table must have its own alias).
-- Promedio de inscriptos por curso (entre los cursos que tienen alumnos)
SELECT AVG(t.inscriptos) AS promedio
FROM (
SELECT ID_CURSO, COUNT(*) AS inscriptos
FROM alumnos_cursos
GROUP BY ID_CURSO
) AS t;
-- (3 + 1 + 2 + 2) / 4 = 2.00007. CTE con WITH: subconsultas con nombre (MySQL 8.0+)
Una CTE (Common Table Expression) es una tabla derivada a la que le ponés nombre al principio. Se lee de arriba hacia abajo y se puede reutilizar dentro de la misma consulta.
WITH inscriptos AS (
SELECT ID_CURSO, COUNT(*) AS cantidad
FROM alumnos_cursos
GROUP BY ID_CURSO
)
SELECT c.TITULO, i.cantidad
FROM inscriptos AS i
INNER JOIN cursos AS c ON c.ID_CURSO = i.ID_CURSO
WHERE i.cantidad > (SELECT AVG(cantidad) FROM inscriptos);
-- Programación PHP · 3Dato BI
Las CTE son ideales para consultas largas de reportes: cada bloqueWITH es un paso con nombre (totales, ranking, filtros), igual que los pasos de Power Query.8. ¿Subconsulta o JOIN?
| Necesitás… | Usá |
|---|---|
| Mostrar columnas de las dos tablas | JOIN |
| Filtrar por «existe / no existe» en otra tabla | EXISTS / NOT EXISTS |
| Comparar contra un valor calculado (promedio, máximo) | Subconsulta escalar |
| Agregar en dos niveles (inscriptos por curso y luego promedio) | Tabla derivada o WITH |
MySQL 8 suele transformar internamente IN y EXISTS en joins (semijoins), así que elegí la forma más clara de leer y verificá el rendimiento con EXPLAIN (lo vemos en la próxima clase).
9. Práctica
- Listá los artículos con precio menor al promedio de
articulos. - Listá los alumnos que tienen alguna actuación destacada, usando
EXISTS. - Mostrá la descripción del artículo más caro usando una subconsulta con
MAX.
-- Soluciones
-- 1) Promedio = 2,966 → Leche 1L., Agua 5L., Galletas 200 gr., Fregasuelos
SELECT DESCRIPCION, PRECIO FROM articulos
WHERE PRECIO < (SELECT AVG(PRECIO) FROM articulos);
-- 2) Jeremias, Teresa, Sergio
SELECT a.NOMBRE FROM alumnos AS a
WHERE EXISTS (
SELECT 1 FROM actuaciones_destacadas AS ad
WHERE ad.ID_ALUMNO = a.ID_ALUMNO
);
-- 3) Café 250 gr.
SELECT DESCRIPCION FROM articulos
WHERE PRECIO = (SELECT MAX(PRECIO) FROM articulos);Bibliografía y recursos online
- MySQL 8.0 Reference Manual. Subqueries.
- MySQL 8.0 Reference Manual. WITH (Common Table Expressions).
- MySQL 8.0 Reference Manual. Optimizing Subqueries, Derived Tables, View References, and CTEs.
- MySQL Tutorial. MySQL Subquery, Derived Tables y EXISTS.
- W3Schools. MySQL EXISTS y ANY / ALL (con editor para practicar).
- SQLBolt. SQL Topic: Subqueries (gratuito).
- Libro: Grippa, V. M. y Kuzmichev, S. (2021). Learning MySQL (2.ª ed.). O’Reilly Media.
- Libro: Silva, R. (2023). MySQL Crash Course: A Hands-on Introduction to Database Development. No Starch Press.
Los recursos en inglés se pueden leer con el traductor del navegador.
Siguiente clase: Vistas e índices.
