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:

TEXT
    ┌───────┐   ┌───────┐
    │   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.

SQL
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.

SQL
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):

TEXT
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
💡 Dica: LEFT JOIN é um dos tipos de junção mais utilizados, especialmente adequado para "encontrar registros sem dados associados", como "funcionários sem departamento".

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.

SQL
SELECT e.name, d.name AS department
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.id;
⚠️ Nota: SQLite não suporta RIGHT JOIN. Se você precisar do efeito do RIGHT JOIN, troque as duas tabelas e use LEFT JOIN em vez disso:

SQL
-- 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.

SQL
-- 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;
⚠️ SQLite não suporta FULL OUTER JOIN, mas você pode simulá-lo usando LEFT JOIN + UNION + RIGHT JOIN:

SQL
-- 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:

SQL
-- 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.

SQL
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.

💡 Dica: CROSS JOIN é tipicamente usado para gerar matrizes de combinação (por exemplo, modelos de relatório "cada funcionário × cada trimestre"). Tenha cuidado com a explosão de dados ao usá-lo.

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.

SQL
-- 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;
💡 Dica: A tabela em uma auto junção deve usar aliases diferentes para distingui-las (por exemplo, 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:

TEXT
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

SQL
SELECT nome_coluna
FROM tabela_A
LEFT JOIN tabela_B ON tabela_A.coluna = tabela_B.coluna;
💡 Dica: LEFT OUTER JOIN e LEFT JOIN são completamente equivalentes — a palavra-chave OUTER pode ser omitida.

💡 Dica: No LEFT JOIN, a condição ON afeta apenas a correspondência da tabela da direita; todas as linhas da tabela da esquerda são preservadas.

Sintaxe RIGHT JOIN

SQL
SELECT nome_coluna
FROM tabela_A
RIGHT JOIN tabela_B ON tabela_A.coluna = tabela_B.coluna;

Sintaxe CROSS JOIN

SQL
SELECT nome_coluna
FROM tabela_A
CROSS JOIN tabela_B;
-- CROSS JOIN não requer condição ON

Sintaxe SELF JOIN

SQL
SELECT a.coluna, b.coluna
FROM tabela_A a
JOIN tabela_B b ON a.coluna = b.coluna;
💡 Dica: A chave de uma auto junção é usar aliases para "transformar" a mesma tabela em duas tabelas para manipulação.


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:

SQL
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;
▶ Experimente

Saída:

TEXT
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":

SQL
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;
▶ Experimente

Saída (depende dos dados; alguns produtos podem não ter pedidos):

TEXT
produto   category  price     qtd_pedidos
--------  --------  --------  -----------
Mac Mini  Computer  4499.00   0
💡 Abordagem: Primeiro use LEFT JOIN para associar todos os produtos aos pedidos, depois use 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:

SQL
-- 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;
▶ Experimente

Saída:

TEXT
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
💡 Abordagem: Em uma auto junção, a mesma tabela desempenha dois papéis — 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":

SQL
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:

SQL
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:

TEXT
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
💡 Chave: 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


📝 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!

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%