Queria voltar rapidinho no assunto de subqueries no SELECT. Principalmente porque a query interna pode acessar dados da query externa. Parece simples, mas nem tanto. Bora mergulhar de novo nesse tema...
Subqueries no SELECT deixam você adicionar colunas extras com valores calculados ou dados que dependem de outros registros ou tabelas. Por exemplo, dá pra mostrar uma lista de estudantes com a média das notas deles, quantos cursos eles estão fazendo ou a nota máxima atual do grupo. Isso é útil quando você quer analisar dados "na hora", criando colunas-resumo sem precisar processar tudo antes.
O básico das subqueries no SELECT
Antes de ir pros exemplos, bora ver a sintaxe geral. Subqueries no SELECT ficam assim:
SELECT column1,
column2,
(SELECT agregacao_ou_condicao FROM outra_tabela WHERE condicao) AS novo_nome_coluna
FROM tabela_principal;
Repara que a subquery retorna um valor só, que aparece no resultado como uma nova coluna. E o condicao pode acessar colunas da tabela_principal.
Exemplo 1: Adicionando a média das notas do estudante
Bora começar com uma query simples e útil: temos a tabela students e a tabela grades, onde ficam as notas dos estudantes.
Tabela students:
| id | name |
|---|---|
| 1 | Alex Lin |
| 2 | Anna Song |
| 3 | Dan Seth |
Tabela grades:
| student_id | grade |
|---|---|
| 1 | 90 |
| 1 | 85 |
| 2 | 76 |
| 3 | 88 |
| 3 | 92 |
Agora queremos pegar a lista de estudantes com o nome e a média das notas. Pra isso, usamos uma subquery no SELECT:
SELECT
s.id,
s.name,
(SELECT AVG(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS average_grade
FROM students s;
Resultado:
| id | name | average_grade |
|---|---|---|
| 1 | Alex Lin | 87.5 |
| 2 | Anna Song | 76.0 |
| 3 | Dan Seth | 90.0 |
Aqui a subquery (SELECT AVG(g.grade) FROM grades g WHERE g.student_id = s.id) calcula a média das notas pra cada estudante. Ela retorna um valor pra cada linha da tabela students, e isso é prático quando você não quer fazer JOIN ou criar views antes.
Exemplo 2: Contando quantos cursos cada estudante faz
Agora bora adicionar info dos estudantes: quantos cursos eles estão fazendo. Pra isso temos mais tabelas:
Tabela enrollments:
| student_id | course_id |
|---|---|
| 1 | 101 |
| 1 | 102 |
| 2 | 101 |
Vamos mostrar a lista de estudantes com a quantidade de cursos em que estão inscritos:
SELECT
s.id,
s.name,
(SELECT COUNT(*)
FROM enrollments e
WHERE e.student_id = s.id) AS course_count -- referência pra tabela students da query externa
FROM students s;
Resultado:
| id | name | course_count |
|---|---|---|
| 1 | Alex Lin | 2 |
| 2 | Anna Song | 1 |
| 3 | Dan Seth | 0 |
A subquery (SELECT COUNT(*) FROM enrollments e WHERE e.student_id = s.id) conta quantos registros tem na tabela enrollments pra cada estudante.
Agregando dados em subqueries
É bem comum usar subqueries no SELECT pra calcular dados agregados. Funções como AVG, SUM, COUNT, MAX, MIN deixam você processar dados direto dentro de outras queries.
Exemplo 3: Soma total das notas do estudante
Bora adicionar a soma total das notas pra cada estudante. Pra isso, usamos uma subquery que soma todas as notas da tabela grades:
SELECT
s.id,
s.name,
(SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS total_grade
FROM students s;
Resultado:
| id | name | total_grade |
|---|---|---|
| 1 | Alex Lin | 175 |
| 2 | Anna Song | 76 |
| 3 | Dan Seth | 180 |
Essa subquery (SELECT SUM(g.grade) FROM grades g WHERE g.student_id = s.id) soma as notas de cada estudante. Se o estudante não tiver nota, o resultado é NULL, porque SUM retorna NULL quando não tem valor nenhum.
Limitações e dicas
- Performance. Subqueries no
SELECTrodam separadas pra cada linha da tabela principal. Isso pode deixar tudo bem lento se o volume de dados for grande. Se der, troca porJOINou usa dados agregados já prontos. Tipo assim:
SELECT
s.id,
s.name,
g.total_grade
FROM students s
LEFT JOIN (
SELECT student_id, SUM(grade) AS total_grade
FROM grades
GROUP BY student_id
) g ON s.id = g.student_id;
Esse jeito com JOIN é mais rápido, porque a agregação e soma rolam só uma vez.
2. Problemas com NULL.
Se não tiver dado na subquery, o resultado vai ser NULL. Isso pode pegar de surpresa. Exemplo:
SELECT
s.id,
s.name,
(SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS total_grade
FROM students s;
Se o estudante não tiver registro em grades, o total_grade vai ser NULL. Pra trocar o NULL por 0, usa a função COALESCE:
SELECT
s.id,
s.name,
COALESCE((SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id), 0) AS total_grade
FROM students s;
Sim, aqui o primeiro parâmetro da função COALESCE é
(
SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id
)
Otimizando subqueries no SELECT
Pra evitar cálculos desnecessários e deixar tudo mais rápido:
- Cria índices nas colunas usadas nas subqueries. Por exemplo, indexar
student_idna tabelagradesacelera o filtro. - Troca subqueries por dados agregados já prontos usando
JOIN, se der. - Limita a quantidade de dados processados nas subqueries usando filtro (
WHERE).
Exemplo final: juntando várias subqueries
Bora juntar tudo e criar uma query que mostra o nome do estudante, média das notas, quantidade de cursos e soma total das notas:
SELECT
s.id,
s.name,
(SELECT AVG(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS average_grade,
(SELECT COUNT(*)
FROM enrollments e
WHERE e.student_id = s.id) AS course_count,
(SELECT SUM(g.grade)
FROM grades g
WHERE g.student_id = s.id) AS total_grade
FROM students s;
Essa query retorna o perfil completo do estudante, usando o poder das subqueries. A gente vê a média e soma das notas, e também quantos cursos cada um faz. Esse tipo de query é ótimo pra pegar info agregada rapidinho, sem precisar criar VIEW ou fazer JOIN separado.
| id | name | average_grade | course_count | total_grade |
|---|---|---|---|---|
| 1 | Alex Lin | 87.5 | 2 | 175 |
| 2 | Anna Song | 76.0 | 1 | 76 |
| 3 | Dan Seth | 90.0 | 0 | 180 |
GO TO FULL VERSION