Ejemplos de consultas anidadas complejas: combinación de EXISTS, IN, HAVING
¡Enhorabuena, hemos llegado al punto donde esto se pone realmente interesante! Hoy vamos a ver cómo combinar diferentes tipos de subconsultas para resolver tareas complicadas. EXISTS, IN, HAVING — ese trío mágico que te hará sentir como un mago de las bases de datos. Vamos a sacar datos de una tabla, filtrarlos usando otra, agruparlos y luego filtrar los grupos. Y como bonus, veremos algunos trucos para hacer las consultas más eficientes.
Vamos a empezar planteando una tarea general que iremos resolviendo a lo largo de la lección.
Planteamiento del problema
Supongamos que tenemos una base de datos de una universidad con tres tablas:
Tabla students
| id | name | group_id |
|---|---|---|
| 1 | Otto | 101 |
| 2 | Maria | 101 |
| 3 | Alex | 102 |
| 4 | Anna | 103 |
Tabla courses
| id | name |
|---|---|
| 1 | Matemáticas |
| 2 | Programación |
| 3 | Filosofía |
Tabla enrollments
| student_id | course_id | grade |
|---|---|---|
| 1 | 1 | 90 |
| 1 | 2 | NULL |
| 2 | 1 | 85 |
| 3 | 3 | 70 |
Hay que seleccionar todos los estudiantes que:
- Están inscritos al menos en un curso
EXISTS. - No tienen nota en al menos uno de los cursos en los que están inscritos
IN. - Pertenecen a grupos donde la nota media es mayor que 80
HAVING.
Solución usando EXISTS y IN
Paso 1: Comprobar estudiantes inscritos (EXISTS). Empezamos con la condición más sencilla. Queremos saber quién está inscrito al menos en un curso. Para esto usamos EXISTS.
SELECT name
FROM students s
WHERE EXISTS (
SELECT 1
FROM enrollments e
WHERE e.student_id = s.id
);
- La consulta externa selecciona los nombres de la tabla
students. - En la subconsulta comprobamos si existen registros en la tabla
enrollmentsque correspondan al estudiante de la consulta externa (WHERE e.student_id = s.id). SELECT 1se usa para indicar que solo nos importa la existencia del registro, no su contenido.
Resultado:
| name |
|---|
| Otto |
| Maria |
| Alex |
Ahora ya sabemos quiénes están inscritos en cursos. Pero queremos más. Queremos filtrarlos por ausencia de notas.
Paso 2: Comprobar ausencia de nota (IN + NULL). Ahora añadimos un filtro: solo queremos los estudiantes que no tienen nota en al menos uno de sus cursos. Aquí nos ayudan IN y saber cómo funciona NULL.
SELECT name
FROM students s
WHERE id IN (
SELECT e.student_id
FROM enrollments e
WHERE e.grade IS NULL
);
- En la consulta externa seleccionamos los nombres de los estudiantes.
- La subconsulta genera una lista de
student_idde la tablaenrollmentsdondegrade IS NULL.
Resultado:
| name |
|---|
| Otto |
Así que Otto es el único estudiante que tiene un curso sin nota. ¡Qué dramático! Pero aún no hemos terminado: solo queremos los grupos donde la nota media es mayor que 80.
Solución usando HAVING
Paso 3: Agrupar y filtrar con HAVING.
Ahora toca unirlo todo. Necesitamos:
- Calcular la nota media de cada grupo.
- Filtrar los grupos con nota media mayor que 80.
- Mostrar los estudiantes de esos grupos, cumpliendo las condiciones anteriores.
SELECT name
FROM students s
WHERE s.group_id IN (
SELECT group_id
FROM students
JOIN enrollments ON students.id = enrollments.student_id
WHERE grade IS NOT NULL
GROUP BY group_id
HAVING AVG(grade) > 80
)
AND id IN (
SELECT e.student_id
FROM enrollments e
WHERE e.grade IS NULL
);
- La consulta externa selecciona los nombres de los estudiantes que cumplen todas las condiciones.
- La primera subconsulta en
WHEREdevuelve la lista degroup_idpara los grupos con nota media mayor que 80.- Unimos
studentsconenrollmentspara obtener las notas. - Filtramos solo los registros donde
grade IS NOT NULL. - Agrupamos los datos por
group_id. - Usamos
HAVINGpara filtrar los grupos.
- Unimos
- La segunda subconsulta en
WHEREcomprueba que el estudiante tiene al menos un curso sin nota. - Ambas partes de la condición se combinan con
AND.
Resultado:
| name |
|---|
| Otto |
Así que hemos descubierto que Otto no solo es el único estudiante sin nota, sino que además pertenece a un grupo que destaca por sus éxitos.
Comparación de enfoques: EXISTS vs IN
EXISTS va genial cuando solo quieres comprobar rápido si existen registros. Es eficiente porque para la búsqueda en cuanto encuentra el primero. Esto es clave en tablas grandes.
Por otro lado, IN es útil cuando te interesa el contenido de los datos. Por ejemplo, si sacas una lista de identificadores (id) para filtrar después. Pero ojo, IN puede ser lento si la subconsulta devuelve muchos valores.
Cuándo usar HAVING
Para datos agregados, cuando necesitas filtrar según los resultados, HAVING es la mejor opción. Pero si puedes mover la condición a WHERE (por ejemplo, filtrando por columna), la consulta será más simple y rápida.
Ejemplo completo
Para afianzar, veamos otro ejemplo: seleccionar los grupos donde al menos un estudiante tiene nota menor que 75, pero que no están inscritos en el curso "Filosofía".
Recordemos nuestras tablas:
Tabla students
| id | name | group_id |
|---|---|---|
| 1 | Otto | 101 |
| 2 | Maria | 101 |
| 3 | Alex | 102 |
| 4 | Anna | 103 |
Tabla courses
| id | name |
|---|---|
| 1 | Matemáticas |
| 2 | Programación |
| 3 | Filosofía |
Tabla enrollments
| student_id | course_id | grade |
|---|---|---|
| 1 | 1 | 90 |
| 1 | 2 | NULL |
| 2 | 1 | 85 |
| 3 | 3 | 70 |
SELECT DISTINCT group_id
FROM students s
WHERE group_id IN (
SELECT s.group_id
FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.grade < 75
)
AND group_id NOT IN (
SELECT s.group_id -- subconsulta de primer nivel
FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.course_id = (
SELECT id FROM courses WHERE name = 'Filosofía' -- subconsulta de segundo nivel :P
)
);
- La primera subconsulta selecciona los grupos donde hay estudiantes con nota menor que 75.
- La segunda subconsulta excluye los grupos relacionados con el curso "Filosofía".
- Combinamos las condiciones usando
INyNOT INpara obtener el resultado final.
Resultado:
| group_id |
|---|
| 101 |
¿Qué tan útil es esto?
En la vida real, estos enfoques te salvan cuando tienes que analizar relaciones complejas de datos. Por ejemplo:
- En analítica para destacar grupos "especiales" de clientes (VIP, problemáticos, etc.).
- En desarrollo de sistemas de recomendación, donde filtramos usuarios por muchos criterios.
- En entrevistas, cuando te piden optimizar una consulta SQL complicada.
¡Practica! Así es como se llega a ser crack.
GO TO FULL VERSION