🧚 Ei! O Beekeeper Studio é uma GUI de banco de dados rápida, moderna e open source Baixar
2 de julho de 2025 Por Matthew Rathbone

Os JOINs do SQL combinam linhas de duas tabelas comparando uma coluna que elas têm em comum, como o author_id de um livro e o id de um autor. O tipo de join decide o que acontece com as linhas que não têm correspondência. Os exemplos funcionam em PostgreSQL, MySQL, SQLite e SQL Server.

Como usar JOINs no SQL

Coloque a segunda tabela depois de JOIN e diga em ON como as linhas dela correspondem às da primeira tabela:

SELECT columns
FROM table_a
JOIN table_b ON table_a.key = table_b.key;

Todos os exemplos desta seção fazem join das mesmas duas tabelas: Butler escreveu dois dos livros, Asimov não escreveu nenhum e ninguém sabe quem escreveu Beowulf.

INNER JOIN

Diagrama de Inner Join

Um INNER JOIN retorna apenas as linhas com correspondência nas duas tabelas. JOIN sozinho significa INNER JOIN.

-- Input: authors
-- id | name
-- ---+--------
-- 1  | Le Guin
-- 2  | Butler
-- 3  | Asimov

-- Input: books
-- title                | author_id
-- ---------------------+----------
-- The Dispossessed     | 1
-- Kindred              | 2
-- Parable of the Sower | 2
-- Beowulf              | NULL

SELECT a.name, b.title
FROM authors a
INNER JOIN books b ON a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower

Asimov não tem livros e Beowulf não tem autor, então nenhum dos dois aparece.

LEFT JOIN

Diagrama de Left Join

Um LEFT JOIN (ou LEFT OUTER JOIN) retorna todas as linhas da tabela da esquerda, aquela depois de FROM. Quando a tabela da direita não tem correspondência, as colunas dela vêm como NULL.

-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a
LEFT JOIN books b ON a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower
-- Asimov  | NULL

RIGHT JOIN

Diagrama de Right Join

Um RIGHT JOIN (ou RIGHT OUTER JOIN) é o espelho: todas as linhas da tabela da direita, com NULLs onde a tabela da esquerda não tem correspondência.

-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a
RIGHT JOIN books b ON a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower
-- NULL    | Beowulf

FULL OUTER JOIN

Diagrama de Full Outer Join

Um FULL OUTER JOIN retorna todas as linhas das duas tabelas, combinadas quando possível e completadas com NULLs quando não. O MySQL não tem esse join: veja “Sintaxe alternativa” abaixo.

-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a
FULL OUTER JOIN books b ON a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower
-- NULL    | Beowulf
-- Asimov  | NULL

CROSS JOIN

Diagrama de Cross Join

Um CROSS JOIN combina cada linha de uma tabela com cada linha da outra, por isso não tem ON. Dois tamanhos e duas cores geram quatro combinações; 1.000 linhas por 1.000 linhas geram um milhão, então use em tabelas pequenas.

-- Input: sizes
-- size
-- ----
-- S
-- M

-- Input: colors
-- color
-- -----
-- Red
-- Blue

SELECT s.size, c.color
FROM sizes s
CROSS JOIN colors c;

-- Output:
-- size | color
-- -----+------
-- S    | Red
-- S    | Blue
-- M    | Red
-- M    | Blue

LEFT ANTI JOIN

Diagrama de Left Anti Join

Um left anti join retorna as linhas da tabela da esquerda que não têm correspondência na tabela da direita. Não existe palavra-chave para isso: faça um LEFT JOIN e mantenha as linhas em que o lado direito veio NULL.

-- Input: the authors and books tables above

SELECT a.name
FROM authors a
LEFT JOIN books b ON a.id = b.author_id
WHERE b.author_id IS NULL;

-- Output:
-- name
-- ------
-- Asimov

RIGHT ANTI JOIN

Diagrama de Right Anti Join

Um right anti join retorna as linhas da tabela da direita que não têm correspondência na tabela da esquerda.

-- Input: the authors and books tables above

SELECT b.title
FROM authors a
RIGHT JOIN books b ON a.id = b.author_id
WHERE a.id IS NULL;

-- Output:
-- title
-- -------
-- Beowulf

Regras importantes

  • NULL nunca corresponde a nada, nem mesmo a outro NULL. O author_id NULL de Beowulf não corresponde a nenhum autor, então só os joins que mantêm livros sem correspondência o retornam.
  • Uma linha aparece uma vez para cada linha com que corresponde. Butler aparece duas vezes acima porque corresponde a dois livros. Se você somar uma coluna da tabela de autores, os valores dela contam duas vezes.
  • Em um outer join, uma condição sobre a outra tabela deve ficar no ON. No WHERE, ela roda depois do join e descarta as linhas NULL que o join acabou de manter:
-- Input: the authors and books tables above

-- Only Butler is left: the WHERE drops every author whose title is NULL
SELECT a.name, b.title
FROM authors a
LEFT JOIN books b ON a.id = b.author_id
WHERE b.title LIKE 'K%';

-- Output:
-- name   | title
-- -------+--------
-- Butler | Kindred

-- Every author is kept, with a book only where it starts with K
SELECT a.name, b.title
FROM authors a
LEFT JOIN books b ON a.id = b.author_id AND b.title LIKE 'K%';

-- Output:
-- name    | title
-- --------+--------
-- Le Guin | NULL
-- Butler  | Kindred
-- Asimov  | NULL

Sintaxe alternativa

O MySQL não tem FULL OUTER JOIN. Em vez disso, combine um LEFT JOIN com as linhas sem correspondência de um RIGHT JOIN:

-- MySQL
-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a LEFT JOIN books b ON a.id = b.author_id
UNION ALL
SELECT a.name, b.title
FROM authors a RIGHT JOIN books b ON a.id = b.author_id
WHERE a.id IS NULL;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Parable of the Sower
-- Butler  | Kindred
-- Asimov  | NULL
-- NULL    | Beowulf

O SQLite anterior à versão 3.39 não tem RIGHT JOIN nem FULL OUTER JOIN: inverta as tabelas e use um LEFT JOIN.

Um anti join também pode ser escrito com NOT EXISTS. Evite NOT IN para isso: se a subquery retornar um único NULL, como o author_id de Beowulf aqui, o NOT IN não retorna absolutamente nada:

-- Input: the authors and books tables above

SELECT a.name
FROM authors a
WHERE NOT EXISTS (SELECT 1 FROM books b WHERE b.author_id = a.id);

-- Output:
-- name
-- ------
-- Asimov

SELECT a.name
FROM authors a
WHERE a.id NOT IN (SELECT author_id FROM books);

-- Output:
-- name
-- ----
-- (0 rows)

Quando as colunas de correspondência têm o mesmo nome nas duas tabelas, você pode escrever JOIN books USING (author_id) no lugar da condição ON, exceto no SQL Server.

Códigos mais antigos costumam listar as tabelas separadas por vírgulas e colocar a correspondência no WHERE. Isso é um INNER JOIN escrito de outra forma. Sem o WHERE, vira um CROSS JOIN:

-- Input: the authors and books tables above

SELECT a.name, b.title
FROM authors a, books b
WHERE a.id = b.author_id;

-- Output:
-- name    | title
-- --------+---------------------
-- Le Guin | The Dispossessed
-- Butler  | Kindred
-- Butler  | Parable of the Sower

Quando usar JOINs no SQL?

Use um join sempre que os dados de que você precisa estiverem divididos entre tabelas. Qual join usar depende do que deve acontecer com as linhas sem correspondência, e a maioria dos problemas se encaixa em um destes cinco casos.

Uma linha de funcionário guarda o ID do departamento, mas o relatório precisa do nome do departamento. Um INNER JOIN busca esse nome e descarta qualquer linha que não tenha o que buscar.

Manter linhas sem correspondência

Quando os dados relacionados são opcionais, como um funcionário que ainda não foi alocado a um departamento, um INNER JOIN o esconderia sem avisar. Um LEFT JOIN mantém todas as linhas da tabela principal e preenche as lacunas com NULL.

Listar todos os itens, inclusive os vazios

Relatórios muitas vezes precisam de todos os departamentos, categorias ou clientes, inclusive os que ainda não têm nada. Faça dessa tabela aquela cujas linhas são todas mantidas: a primeira com um LEFT JOIN, ou a segunda com um RIGHT JOIN.

Encontrar linhas sem correspondência

Clientes que nunca fizeram pedidos, produtos nunca vendidos, funcionários sem departamento: um anti join encontra o que está faltando, o que também o torna uma boa verificação de qualidade de dados.

Gerar todas as combinações

Um CROSS JOIN gera linhas que ainda não existem: cada tamanho em cada cor, cada funcionário com cada curso de treinamento ou cada loja em cada dia de um relatório.

Exemplos reais de JOINs no SQL

Alunos e seus cursos

Um relacionamento muitos-para-muitos precisa de dois INNER JOINs, passando pela tabela de junção que liga alunos a cursos. Mei não está matriculada em nada, então não aparece na lista:

-- Input: students
-- id | name
-- ---+------
-- 1  | Amara
-- 2  | Lucas
-- 3  | Mei

-- Input: enrollments
-- student_id | course_id | grade
-- -----------+-----------+------
-- 1          | 10        | A
-- 1          | 20        | B
-- 2          | 10        | B

-- Input: courses
-- id | course_name
-- ---+------------
-- 10 | Databases
-- 20 | Statistics

SELECT s.name, c.course_name, e.grade
FROM students s
INNER JOIN enrollments e ON s.id = e.student_id
INNER JOIN courses c ON e.course_id = c.id;

-- Output:
-- name  | course_name | grade
-- ------+-------------+------
-- Amara | Databases   | A
-- Amara | Statistics  | B
-- Lucas | Databases   | B

Usuários com perfis opcionais

Nem todo usuário preencheu um perfil. Um LEFT JOIN mantém todos eles:

-- Input: users
-- id | username
-- ---+---------
-- 1  | rosa_m
-- 2  | devon
-- 3  | kai_l

-- Input: user_profiles
-- user_id | bio
-- --------+------------------------
-- 1       | Data engineer in Lisbon
-- 3       | Loves SQL puzzles

SELECT u.username, p.bio
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id;

-- Output:
-- username | bio
-- ---------+------------------------
-- rosa_m   | Data engineer in Lisbon
-- devon    | NULL
-- kai_l    | Loves SQL puzzles

Contagem de pedidos de cada cliente

Para incluir clientes sem pedidos, faça um LEFT JOIN a partir de customers. Depois, conte uma coluna da tabela de pedidos: COUNT(*) conta a linha que o LEFT JOIN criou para Omar, então ele ficaria com 1 em vez de 0:

-- Input: customers
-- id | name
-- ---+------------
-- 1  | Harper Lee
-- 2  | Omar Haddad
-- 3  | Yuki Tanaka

-- Input: orders
-- id  | customer_id | total
-- ----+-------------+-------
-- 101 | 1           | 59.90
-- 102 | 3           | 120.00
-- 103 | 1           | 14.50

-- Wrong: COUNT(*) counts Omar's NULL row
SELECT c.name, COUNT(*) AS order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY c.id;

-- Output:
-- name        | order_count
-- ------------+------------
-- Harper Lee  | 2
-- Omar Haddad | 1
-- Yuki Tanaka | 1

-- Right: COUNT(o.id) skips it
SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name
ORDER BY c.id;

-- Output:
-- name        | order_count
-- ------------+------------
-- Harper Lee  | 2
-- Omar Haddad | 0
-- Yuki Tanaka | 1

Faturas e pagamentos que não batem

Um FULL OUTER JOIN que mantém apenas as linhas sem correspondência confere duas listas entre si em uma única query: a fatura 502 não foi paga, e o pagamento 9003 é de uma fatura que não existe:

-- Input: invoices
-- invoice_id | amount
-- -----------+-------
-- 501        | 120.00
-- 502        | 75.50
-- 503        | 300.00

-- Input: payments
-- payment_id | invoice_id | paid
-- -----------+------------+-------
-- 9001       | 501        | 120.00
-- 9002       | 503        | 300.00
-- 9003       | 504        | 42.00

-- MySQL has no FULL OUTER JOIN: use the UNION ALL form under "Alternative syntax"
SELECT i.invoice_id, i.amount, p.payment_id, p.invoice_id AS paid_invoice
FROM invoices i
FULL OUTER JOIN payments p ON p.invoice_id = i.invoice_id
WHERE i.invoice_id IS NULL OR p.payment_id IS NULL;

-- Output:
-- invoice_id | amount | payment_id | paid_invoice
-- -----------+--------+------------+-------------
-- 502        | 75.50  | NULL       | NULL
-- NULL       | NULL   | 9003       | 504

Faturamento diário de cada loja, inclusive dias sem vendas

Faça um CROSS JOIN das lojas com os dias para obter uma linha por loja por dia e depois um LEFT JOIN das vendas nessa grade. York não vendeu nada em 6 de outubro e mesmo assim tem uma linha:

-- Input: stores
-- store
-- -----
-- Leeds
-- York

-- Input: days
-- day
-- ----------
-- 2026-10-05
-- 2026-10-06

-- Input: sales
-- store | day        | amount
-- ------+------------+-------
-- Leeds | 2026-10-05 | 410.00
-- Leeds | 2026-10-06 | 385.50
-- York  | 2026-10-05 | 220.00

SELECT st.store, d.day, COALESCE(SUM(s.amount), 0.00) AS takings
FROM stores st
CROSS JOIN days d
LEFT JOIN sales s ON s.store = st.store AND s.day = d.day
GROUP BY st.store, d.day
ORDER BY st.store, d.day;

-- Output:
-- store | day        | takings
-- ------+------------+--------
-- Leeds | 2026-10-05 | 410.00
-- Leeds | 2026-10-06 | 385.50
-- York  | 2026-10-05 | 220.00
-- York  | 2026-10-06 | 0.00

Leitura complementar sobre JOINs no SQL

Neste blog:

Documentação oficial:

Beekeeper Studio é um cliente de banco de dados gratuito e open source

A melhor ferramenta de consultas e edição de SQL que já usei. Tem tudo o que preciso para gerenciar meu banco de dados. - ⭐⭐⭐⭐⭐ Mit

O Beekeeper Studio é rápido, intuitivo e fácil de usar. Ele suporta vários bancos de dados e funciona muito bem no Windows, Mac e Linux.

A versão do Beekeeper para Linux tem 100% dos recursos, sem atalhos e sem concessões.

O que os usuários dizem sobre o Beekeeper Studio

★★★★★
"O Beekeeper Studio substituiu completamente meu antigo fluxo de trabalho com SQL. É rápido, intuitivo e torna o trabalho com bancos de dados prazeroso de novo."
— Alex K., desenvolvedor de banco de dados
★★★★★
"Já testei várias GUIs de banco de dados, mas o Beekeeper encontra o equilíbrio perfeito entre recursos e simplicidade. Simplesmente funciona."
— Sarah M., engenheira full stack

Pronto para melhorar seu fluxo de trabalho com SQL?

download Baixar grátis