CodeGym /Cursos /SQL SELF /Exemplos de consultas aninhadas avançadas: combinando EXI...

Exemplos de consultas aninhadas avançadas: combinando EXISTS, IN, HAVING

SQL SELF
Nível 14 , Lição 3
Disponível

Parabéns, chegamos naquele ponto em que a coisa fica realmente massa! Hoje vamos ver como combinar vários tipos de subqueries pra resolver problemas mais complexos. EXISTS, IN, HAVING — esse trio vai fazer você se sentir um mago dos bancos de dados. Vamos pegar dados de uma tabela, filtrar usando outra, agrupar, e depois filtrar os grupos. E de quebra, vamos sacar umas dicas pra deixar as queries mais eficientes.

Bora começar definindo um desafio geral, que a gente vai resolvendo ao longo da aula.

Definição do problema

Imagina que temos um banco de dados da universidade com três tabelas:

Tabela students

id name group_id
1 Otto 101
2 Maria 101
3 Alex 102
4 Anna 103

Tabela courses

id name
1 Matemática
2 Programação
3 Filosofia

Tabela enrollments

student_id course_id grade
1 1 90
1 2 NULL
2 1 85
3 3 70

Precisamos selecionar todos os estudantes que:

  1. Estão matriculados em pelo menos um curso EXISTS.
  2. Não têm nota em pelo menos um dos cursos em que estão matriculados IN.
  3. Pertencem a grupos onde a média das notas é maior que 80 HAVING.

Resolvendo com EXISTS e IN

Passo 1: Checar estudantes matriculados (EXISTS). Vamos começar pelo mais simples. Precisamos saber quem está matriculado em pelo menos um curso. Pra isso, rola usar EXISTS.

SELECT name
FROM students s
WHERE EXISTS (
  SELECT 1
  FROM enrollments e
  WHERE e.student_id = s.id
);
  1. A query principal pega os nomes da tabela students.
  2. No subquery, a gente verifica se tem registros na tabela enrollments pro estudante do select principal (WHERE e.student_id = s.id).
  3. SELECT 1 só serve pra dizer que a gente só quer saber se existe registro, não o conteúdo.

Resultado:

name
Otto
Maria
Alex

Agora já sabemos quem está matriculado em algum curso. Mas queremos mais. Queremos filtrar quem não tem nota em algum curso.

Passo 2: Checar ausência de nota (IN + NULL). Agora vamos filtrar: só queremos estudantes que têm pelo menos um curso sem nota. Aqui entra o IN e o truque do NULL.

SELECT name
FROM students s
WHERE id IN (
  SELECT e.student_id
  FROM enrollments e
  WHERE e.grade IS NULL
);
  1. No select principal, pegamos os nomes dos estudantes.
  2. O subquery monta uma lista de student_id da tabela enrollments onde grade IS NULL.

Resultado:

name
Otto

Então, Otto é o único estudante que tem um curso sem nota. Que drama! Mas ainda não acabou: só queremos considerar grupos com média acima de 80.

Resolvendo com HAVING

Passo 3: Agrupando e filtrando com HAVING.

Agora é hora de juntar tudo. Precisamos:

  1. Calcular a média das notas pra cada grupo.
  2. Filtrar grupos com média acima de 80.
  3. Mostrar estudantes desses grupos, considerando as condições 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
);
  1. A query principal pega os nomes dos estudantes que passam em todas as condições.
  2. O primeiro subquery no WHERE retorna os group_id dos grupos com média acima de 80.
    • Juntamos students com enrollments pra pegar as notas.
    • Filtramos só onde grade IS NOT NULL.
    • Agrupamos por group_id.
    • Usamos HAVING pra filtrar os grupos.
  3. O segundo subquery no WHERE checa se o estudante tem pelo menos um curso sem nota.
  4. As duas condições são combinadas com AND.

Resultado:

name
Otto

Então, descobrimos que Otto não só é o único sem nota, mas também faz parte de um grupo que manda bem nas notas.

Comparando abordagens: EXISTS vs IN

EXISTS é top quando você só quer saber se existe registro. Ele é eficiente porque para de procurar assim que acha o primeiro. Isso faz diferença em tabelas grandes.

Já o IN é útil quando o foco é o conteúdo. Tipo, se você quer uma lista de id pra filtrar depois. Mas cuidado: IN pode ficar lento se o subquery retorna muitos valores.

Quando usar HAVING

Pra dados agregados, quando você precisa filtrar pelo resultado, HAVING é o caminho. Mas se der pra jogar a condição no WHERE (tipo filtrar por coluna), a query fica mais simples e rápida.

Exemplo completo

Pra fixar, bora ver outro exemplo: selecionar grupos onde pelo menos um estudante tem nota menor que 75, mas ninguém está matriculado no curso "Filosofia".

Lembrando das nossas tabelas:

Tabela students

id name group_id
1 Otto 101
2 Maria 101
3 Alex 102
4 Anna 103

Tabela courses

id name
1 Matemática
2 Programação
3 Filosofia

Tabela 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                                 -- subquery de 1º nível
  FROM students s
  JOIN enrollments e ON s.id = e.student_id
  WHERE e.course_id = (
    SELECT id FROM courses WHERE name = 'Filosofia' -- subquery de 2º nível :P
  )
);
  1. O primeiro subquery pega os grupos onde tem estudante com nota menor que 75.
  2. O segundo subquery exclui grupos ligados ao curso "Filosofia".
  3. Combinamos as condições com IN e NOT IN pra chegar no resultado final.

Resultado:

group_id
101

Isso é útil mesmo?

No mundo real, essas técnicas salvam quando você precisa analisar relações complexas nos dados. Por exemplo:

  • Na análise pra separar grupos "especiais" de clientes (VIP, problemáticos, etc).
  • No desenvolvimento de sistemas de recomendação, quando você filtra usuários por vários critérios.
  • Em entrevistas, quando te pedem pra otimizar uma query SQL cabulosa.

Pratique! Esse é o caminho pra virar ninja.

2
Tarefa
SQL SELF, nível 14, lição 3
Bloqueado
Encontrando estudantes com nota ausente
Encontrando estudantes com nota ausente
2
Tarefa
SQL SELF, nível 14, lição 3
Bloqueado
Agrupamento e filtragem usando HAVING
Agrupamento e filtragem usando HAVING
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION