Tipos de Junção Explicados
Tipos de Junção Explicados
Imagine que você administra uma cafeteria e precisa lidar com três situações todos os dias: clientes que são membros e fizeram compras (ambas as tabelas têm dados), membros que não fizeram compras (tabela da esquerda tem dados, tabela da direita não), e clientes que compraram mas não se cadastraram como membros (tabela da direita tem dados, tabela da esquerda não). O SQL fornece diferentes tipos de JOINs para lidar precisamente com esses (e outros) cenários.
1. Conceitos Fundamentais
Visão Geral dos Seis Tipos de Junção
| Tipo de Junção | Descrição | Retorna |
|---|---|---|
| INNER JOIN | Junção interna | Retorna apenas linhas que correspondem em ambas as tabelas |
| LEFT JOIN | Junção à esquerda | Retorna todas as linhas da tabela da esquerda + linhas correspondentes da tabela da direita (NULL se não houver correspondência) |
| RIGHT JOIN | Junção à direita | Retorna todas as linhas da tabela da direita + linhas correspondentes da tabela da esquerda (NULL se não houver correspondência) |
| FULL OUTER JOIN | Junção externa completa | Retorna todas as linhas de ambas as tabelas (NULL onde não houver correspondência) |
| CROSS JOIN | Junção cruzada | Retorna o produto cartesiano de ambas as tabelas (todas as combinações) |
| SELF JOIN | Auto junção | Uma tabela unida consigo mesma |
Compreendendo JOINs com Diagramas de Venn
Use dois círculos A e B para representar duas tabelas:
┌───────┐ ┌───────┐
│ A │ │ B │
│ │ │ │
│ ┌───┼───┼───┐ │
│ │ A ∩ B │ │ │
│ └───┼───┼───┘ │
│ │ │ │
└───────┘ └───────┘
INNER JOIN → A ∩ B (interseção)
LEFT JOIN → A (todo A + A∩B)
RIGHT JOIN → B (todo B + A∩B)
FULL JOIN → A ∪ B (união)
CROSS JOIN → A × B (todas as combinações)
INNER JOIN
Mantém apenas linhas que correspondem em ambas as tabelas. Abordado em detalhes na lição anterior, então não repetiremos aqui.
SELECT e.name, d.name AS department
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;
LEFT JOIN
Retorna todas as linhas da tabela da esquerda, com as colunas da tabela da direita mostrando NULL onde não há correspondência. A tabela da esquerda é a primeira tabela após FROM.
SELECT e.name, d.name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;
Saída (observe que o departamento de Zhou Jiu é NULL):
name department
-------- ----------
Zhang San Technology
Li Si Technology
Wang Wu Marketing
Zhao Liu Finance
Qian Qi Technology
Sun Ba Marketing
Zhou Jiu NULL
Wu Shi Finance
RIGHT JOIN
Retorna todas as linhas da tabela da direita, com as colunas da tabela da esquerda mostrando NULL onde não há correspondência. A tabela da direita é a tabela após JOIN.
SELECT e.name, d.name AS department
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;
-- Equivalente a RIGHT JOIN departments d ON ...
SELECT e.name, d.name AS department
FROM departments d
LEFT JOIN employees e ON e.department_id = d.id;
FULL OUTER JOIN
Retorna todas as linhas de ambas as tabelas, mostrando NULL onde não há correspondência. É equivalente à união de LEFT JOIN e RIGHT JOIN.
-- Sintaxe (suportada por MySQL/PostgreSQL/SQL Server)
SELECT e.name, d.name AS department
FROM employees e
FULL OUTER JOIN departments d ON e.department_id = d.id;
LEFT JOIN + UNION + RIGHT JOIN:
-- Simular FULL OUTER JOIN com LEFT JOIN UNION RIGHT JOIN
SELECT e.name, d.name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
UNION
SELECT e.name, d.name AS department
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;
Observe também que o SQLite não suporta RIGHT JOIN. No SQLite, você pode implementar assim:
-- FULL OUTER JOIN equivalente no SQLite
SELECT e.name, d.name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
UNION
SELECT e.name, d.name AS department
FROM departments d
LEFT JOIN employees e ON e.department_id = d.id;
💡 Dica: Na prática, FULL OUTER JOIN é usado com pouca frequência. A maioria dos cenários pode ser resolvida com LEFT JOIN, que deve ser sua primeira escolha.
CROSS JOIN
Retorna o produto cartesiano de duas tabelas — cada combinação de cada linha da tabela da esquerda com cada linha da tabela da direita. Contagem de linhas do resultado = linhas da tabela da esquerda × linhas da tabela da direita.
SELECT e.name, d.name AS department
FROM employees e
CROSS JOIN departments d;
Se employees tiver 8 linhas e departments tiver 4 linhas, o resultado terá 8 × 4 = 32 linhas.
SELF JOIN
Uma tabela unida consigo mesma. Comumente usada para consultar "relacionamentos hierárquicos" ou "pareamentos dentro da mesma tabela", como funcionários e seus gerentes.
-- Supondo que a tabela employees tenha um campo manager_id
SELECT e.name AS funcionario, m.name AS gerente
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
e e m), caso contrário o SQL não consegue diferenciar as duas referências.
Árvore de Decisão para Seleção de Junção
Ao enfrentar requisitos de negócio, escolha o tipo de junção com base na seguinte lógica:
Precisa juntar duas tabelas?
├── Apenas dados correspondentes → INNER JOIN
├── Manter todos os dados da tabela da esquerda → LEFT JOIN
├── Manter todos os dados da tabela da direita → RIGHT JOIN
├── Manter todos os dados de ambas as tabelas → FULL OUTER JOIN
├── Precisa de todas as combinações → CROSS JOIN
└── A tabela se relaciona consigo mesma → SELF JOIN
2. Sintaxe/Uso Básico
Sintaxe LEFT JOIN
SELECT nome_coluna
FROM tabela_A
LEFT JOIN tabela_B ON tabela_A.coluna = tabela_B.coluna;
LEFT OUTER JOIN e LEFT JOIN são completamente equivalentes — a palavra-chave OUTER pode ser omitida.
ON afeta apenas a correspondência da tabela da direita; todas as linhas da tabela da esquerda são preservadas.
Sintaxe RIGHT JOIN
SELECT nome_coluna
FROM tabela_A
RIGHT JOIN tabela_B ON tabela_A.coluna = tabela_B.coluna;
Sintaxe CROSS JOIN
SELECT nome_coluna
FROM tabela_A
CROSS JOIN tabela_B;
-- CROSS JOIN não requer condição ON
Sintaxe SELF JOIN
SELECT a.coluna, b.coluna
FROM tabela_A a
JOIN tabela_B b ON a.coluna = b.coluna;
Exemplo: Encontrar Funcionários Sem Departamento (Dificuldade ⭐)
Use LEFT JOIN para encontrar todos os funcionários, incluindo aqueles não atribuídos a um departamento:
SELECT e.name AS funcionario,
COALESCE(d.name, 'Não atribuído') AS departamento
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;
Saída:
funcionario departamento
----------- ------------
Zhang San Technology
Li Si Technology
Wang Wu Marketing
Zhao Liu Finance
Qian Qi Technology
Sun Ba Marketing
Zhou Jiu Não atribuído
Wu Shi Finance
COALESCE(d.name, 'Não atribuído') significa: se d.name for NULL, exibir "Não atribuído".
Exemplo: Encontrar Produtos Sem Pedidos (Dificuldade ⭐⭐)
A tabela de produtos tem 8 produtos, mas nem todos têm pedidos. Use LEFT JOIN para encontrar produtos com "zero vendas":
SELECT p.name AS produto, p.category, p.price,
COUNT(o.id) AS qtd_pedidos
FROM products p
LEFT JOIN orders o ON p.id = o.product_id
GROUP BY p.id, p.name, p.category, p.price
HAVING COUNT(o.id) = 0;
Saída (depende dos dados; alguns produtos podem não ter pedidos):
produto category price qtd_pedidos
-------- -------- -------- -----------
Mac Mini Computer 4499.00 0
GROUP BY para agregar, e finalmente use HAVING para filtrar produtos com zero pedidos.
Exemplo: Auto Junção para Funcionários e Gerentes (Dificuldade ⭐⭐⭐)
Supondo que a tabela employees tenha um campo manager_id. Consultar cada funcionário e seu gerente:
-- Primeiro adicionar o campo manager_id e dados
ALTER TABLE employees ADD COLUMN manager_id INTEGER;
UPDATE employees SET manager_id = 5 WHERE id IN (1, 2, 3, 6);
UPDATE employees SET manager_id = NULL WHERE id = 5;
-- Consulta com auto junção
SELECT e.name AS funcionario,
COALESCE(m.name, 'Sem gerente') AS gerente
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
Saída:
funcionario gerente
----------- --------
Zhang San Qian Qi
Li Si Qian Qi
Wang Wu Qian Qi
Zhao Liu Sem gerente
Qian Qi Sem gerente
Sun Ba Qian Qi
Zhou Jiu Sem gerente
Wu Shi Sem gerente
e representa "si mesmo como funcionário", e m representa "si mesmo como gerente". O relacionamento hierárquico é estabelecido através de manager_id = m.id.
3. Casos de Uso Comuns
Caso 1: Gerar Relatório Matricial Departamento-Funcionário
Use CROSS JOIN para gerar uma matriz completa "cada departamento × cada funcionário":
SELECT d.name AS departamento, e.name AS funcionario
FROM departments d
CROSS JOIN employees e
WHERE e.department_id IS NOT NULL
ORDER BY d.name, e.name;
Isso é útil para cenários como gerar "listas de presença para todos os funcionários".
Caso 2: Encontrar Pares de Funcionários no Mesmo Departamento
Use SELF JOIN para encontrar todos os pares de funcionários no mesmo departamento:
SELECT e1.name AS funcionario1,
e2.name AS funcionario2,
d.name AS departamento
FROM employees e1
INNER JOIN employees e2 ON e1.department_id = e2.department_id
AND e1.id < e2.id
INNER JOIN departments d ON e1.department_id = d.id;
Saída:
funcionario1 funcionario2 departamento
------------ ------------ ------------
Zhang San Li Si Technology
Zhang San Qian Qi Technology
Li Si Qian Qi Technology
Wang Wu Sun Ba Marketing
Zhao Liu Wu Shi Finance
e1.id < e2.id evita pares duplicados (por exemplo, "Zhang San-Li Si" e "Li Si-Zhang San" mantém apenas um) e autopares (por exemplo, "Zhang San-Zhang San").
❓ Perguntas Frequentes
P: Quando LEFT JOIN e INNER JOIN produzem os mesmos resultados? R: Quando não há valores NULL de chave estrangeira na tabela da esquerda (ou seja, todas as linhas encontram correspondência na tabela da direita), LEFT JOIN e INNER JOIN produzem resultados idênticos. Porém, LEFT JOIN adicionalmente mantém linhas da tabela da esquerda que não têm correspondência na tabela da direita.
P: E se o SQLite não suportar RIGHT JOIN e FULL OUTER JOIN? R: RIGHT JOIN pode ser implementado trocando a ordem das tabelas e usando LEFT JOIN. FULL OUTER JOIN pode ser simulado com LEFT JOIN + UNION + LEFT JOIN. Na prática, RIGHT JOIN é raramente usado — LEFT JOIN é suficiente na maioria dos casos.
P: CROSS JOIN pode ser lento? R: Se duas tabelas tiverem 1000 linhas cada, o CROSS JOIN produzirá 1 milhão de linhas de resultado. Portanto, use-o com muita cautela, garantindo que o volume de dados seja gerenciável. CROSS JOIN é frequentemente usado com condições WHERE, o que efetivamente o torna equivalente a INNER JOIN.
P: Quais são os usos práticos de SELF JOIN? R: SELF JOIN é comumente usado para lidar com dados hierárquicos (por exemplo, estruturas organizacionais, respostas de comentários, árvores de categorias) e para encontrar "pareamentos dentro do mesmo grupo" (por exemplo, funcionários do mesmo departamento, clientes da mesma cidade). Sempre que você precisar de "comparação linha a linha dentro da mesma tabela", SELF JOIN pode ser útil.
📖 Resumo
- INNER JOIN retorna apenas linhas correspondentes, LEFT JOIN preserva todas as linhas da tabela da esquerda, RIGHT JOIN preserva todas as linhas da tabela da direita
- FULL OUTER JOIN retorna a união de ambas as tabelas, mas o SQLite não suporta — use LEFT JOIN + UNION para simular
- CROSS JOIN retorna o produto cartesiano; use com cautela em grandes conjuntos de dados
- SELF JOIN conecta uma tabela consigo mesma, adequada para relacionamentos hierárquicos e pareamentos dentro do mesmo grupo
- Árvore de decisão para seleção de junção: use INNER para correspondência, LEFT/RIGHT para preservação, FULL para tudo, CROSS para combinações
- SQLite não suporta RIGHT JOIN e FULL OUTER JOIN, mas podem ser alcançados trocando a ordem das tabelas e combinando com UNION
📝 Exercícios
Exercício 1 (⭐): Escreva uma consulta para exibir todos os departamentos e suas contagens de funcionários, incluindo departamentos sem funcionários. Ordene por contagem de funcionários em ordem decrescente.
Exercício 2 (⭐⭐): Escreva uma consulta para encontrar todos os "clientes que nunca fizeram um pedido". Supondo que precisamos de uma tabela customers, você pode simular usando o campo customer_name na tabela orders — encontre quais clientes aparecem apenas na tabela orders e quais produtos nunca foram pedidos.
Exercício 3 (⭐⭐⭐): Escreva uma consulta usando SELF JOIN para encontrar "pares de funcionários com datas de contratação adjacentes" — os dois funcionários com as datas de contratação mais próximas (dica: use a função julianday() para calcular diferenças de datas).
Próxima Lição
👉 09-subquery - Subconsultas: Aprenda a usar subconsultas em WHERE, FROM e SELECT, além de EXISTS/NOT EXISTS!



