INNER JOIN (ou apenas JOIN) é o comando do SQL para combinar duas tabelas por uma chave compartilhada. O tipo de JOIN mais simples e mais comum — com bancos de dados relacionais você não vai longe sem ele.
Uma analogia rápida. Você tem duas tabelas:
users — usuários (id, name)
orders — pedidos (id, user_id, amount)
Em orders não há nome do comprador — apenas user_id. Para obter pares de "nome do comprador + valor do pedido", você precisa colar essas tabelas de volta por uma chave comum — users.id corresponde a orders.user_id. Essa colagem é o INNER JOIN.
Por que JOIN importa
Um banco de dados normal distribui os dados entre tabelas para evitar a duplicação. O nome do usuário vive apenas em users; orders referencia o usuário através de user_id. Isso se chama normalização.
Quando você precisa mostrar "nome + pedido", precisa montar os dados de volta — é para isso que serve o JOIN. É um dos temas mais importantes do SQL — sem JOIN você não consegue escrever nenhuma consulta do mundo real.
Sintaxe básica
SELECT columns
FROM table_A
INNER JOIN table_B
ON table_A.key = table_B.key;
A palavra INNER é opcional — JOIN sem prefixo significa INNER JOIN. A maioria do código escreve apenas JOIN.
Exemplo: uma loja
Tabela users:
Tabela orders:
| id | user_id | amount | created_at |
|---|
| 10 | 1 | 500 | 2024-03-01 |
| 11 | 1 | 1500 | 2024-03-05 |
| 12 | 2 | 200 | 2024-03-07 |
| 13 | 5 | 3000 | 2024-03-10 |
Consulta — juntar e mostrar o nome do comprador com o valor:
SELECT users.name, orders.amount, orders.created_at
FROM users
INNER JOIN orders
ON users.id = orders.user_id;
Resultado:
| name | amount | created_at |
|---|
| Anna | 500 | 2024-03-01 |
| Anna | 1500 | 2024-03-05 |
| Boris | 200 | 2024-03-07 |
O que aconteceu:
- Cada linha de
users foi pareada com cada linha de orders onde users.id = orders.user_id.
- Anna tem dois pedidos → duas linhas no resultado, com o nome duplicado.
- Boris tem um pedido → uma linha.
- Vera e Grisha não estão lá — eles não têm pedidos. Essa é a característica do INNER JOIN: apenas pares com correspondência em ambas as tabelas.
- O pedido #13 não está lá —
user_id = 5 não existe em users. Às vezes acontece, por exemplo, o usuário foi excluído mas o pedido permaneceu.
Se você também quiser os usuários sem pedidos, isso é LEFT JOIN (artigo separado).
Aliases — nomes curtos de tabela
Escrever users.name, orders.amount toda vez é longo. O código do mundo real usa aliases:
SELECT u.name, o.amount, o.created_at
FROM users u
INNER JOIN orders o
ON u.id = o.user_id;
users u significa "dê à tabela users o alias u". Não muda a lógica — apenas é mais curto. Em consultas grandes com três a cinco tabelas, as consultas sem aliases são ilegíveis.
Observação: os aliases se tornam obrigatórios se ambas as tabelas têm uma coluna com o mesmo nome. Caso contrário o banco de dados não consegue saber a qual id você se refere.
Juntar três ou mais tabelas
Os JOINs se encadeiam: A JOIN B JOIN C. Adicione uma tabela products:
| id | name | price |
|---|
| 100 | Bosch kettle | 49 |
| 101 | iPhone 15 | 999 |
| 102 | Book "Clean Code" | 22 |
E estenda orders com product_id:
| id | user_id | product_id | amount |
|---|
| 10 | 1 | 100 | 49 |
| 11 | 1 | 102 | 22 |
| 12 | 2 | 100 | 49 |
Consulta — quem comprou o quê:
SELECT u.name AS buyer, p.name AS product, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id
INNER JOIN products p ON p.id = o.product_id;
Resultado:
| buyer | product | amount |
|---|
| Anna | Bosch kettle | 49 |
| Anna | Book "Clean Code" | 22 |
| Boris | Bosch kettle | 49 |
Como funciona:
- O banco de dados primeiro junta
users e orders por user_id.
- Depois o resultado é juntado com
products por product_id.
- A saída mantém apenas as linhas onde ambas as junções corresponderam.
Essas cadeias podem ser tão longas quanto você precisar. "Usuários, os seus pedidos, os produtos desses pedidos, as categorias dos produtos e as lojas" — isso são cinco JOINs seguidos.
JOIN com WHERE
JOIN costuma vir com WHERE — JOIN cola as tabelas, WHERE filtra o resultado. Por exemplo, os pedidos da Anna acima de 1000:
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.name = 'Anna'
AND o.amount > 1000;
Uma combinação extremamente comum — quase qualquer consulta de negócio é "junte as tabelas + filtre com WHERE + talvez agregue".
INNER JOIN é a interseção
Em termos de teoria dos conjuntos: INNER JOIN é a interseção de duas tabelas por uma chave. Apenas as linhas presentes em ambas entram.
users orders
┌──┐ ┌──┐
│ │ │ │
│ ●│ ← → │● │ ← pares: apenas estes acabam no resultado
│ │ │ │
└──┘ └──┘
Um usuário sem pedidos — não está no resultado. Um pedido sem usuário (chave estrangeira quebrada) — também não está no resultado.
Exemplo maior: uma biblioteca
books:
| id | title | author_id |
|---|
| 1 | War and Peace | 10 |
| 2 | Anna Karenina | 10 |
| 3 | Crime and Punishment | 11 |
| 4 | The Idiot | 11 |
| 5 | (orphan — author not in DB) | 99 |
authors:
| id | full_name | country |
|---|
| 10 | Leo Tolstoy | Russia |
| 11 | Fyodor Dostoevsky | Russia |
| 12 | Erich Maria Remarque | Germany |
Consulta — cada livro com o nome do autor e o país:
SELECT b.title, a.full_name AS author, a.country
FROM books b
JOIN authors a ON a.id = b.author_id
ORDER BY a.full_name, b.title;
Resultado:
| title | author | country |
|---|
| Anna Karenina | Leo Tolstoy | Russia |
| War and Peace | Leo Tolstoy | Russia |
| Crime and Punishment | Fyodor Dostoevsky | Russia |
| The Idiot | Fyodor Dostoevsky | Russia |
O livro órfão sumiu — author_id = 99 não está em authors. Remarque sumiu — ele tem zero livros na nossa books. O INNER JOIN descartou os dois.
Erros comuns de iniciante
1. Esquecer a cláusula ON. Escrever JOIN orders sem ON — a maioria dos bancos de dados ou dá erro ou recorre a um CROSS JOIN (produto cartesiano, cada linha vezes cada linha). Ou um erro ou uma consulta muito lenta.
2. Colunas erradas no ON. ON u.id = o.id em vez de ON u.id = o.user_id é um erro de digitação clássico. A consulta não vai quebrar — vai retornar absurdos (o usuário #5 pareado com o pedido #5, o que não tem significado).
3. Nomes de coluna ambíguos. Se tanto users quanto orders têm id, e você escreve SELECT id, name FROM users JOIN orders ON …, você obtém "a referência à coluna "id" é ambígua". Use users.id ou aliases.
4. Linhas duplicadas no resultado. Se um usuário tem 5 pedidos, o resultado tem 5 linhas com o nome desse usuário repetido. Não é um bug — é assim que o JOIN funciona. Quer usuários únicos? Envolva em SELECT DISTINCT u.name ou combine com GROUP BY.
5. Achar que o JOIN em si é lento. Os JOINs são rápidos quando há índices nas colunas de junção. É por isso que as colunas de chave estrangeira (user_id e similares) são sempre indexadas em produção.
6. Junções antigas com vírgula. Tecnicamente você pode escrever FROM users, orders WHERE users.id = orders.user_id. Não faça isso. A sintaxe moderna JOIN ... ON ... se lê melhor e separa com clareza a junção da filtragem.
Resumo rápido
INNER JOIN combina duas tabelas por uma chave compartilhada.
- Apenas os pares com correspondência em ambas as tabelas entram no resultado.
- A condição de junção vai depois de
ON.
- A palavra
INNER é opcional; as pessoas escrevem apenas JOIN.
- Você pode encadear muitos JOINs (consultas de três e cinco tabelas são normais).
- Use aliases (
users u) para manter as coisas legíveis.
- Se você também quiser usuários sem pedidos, isso é
LEFT JOIN, um cenário diferente.
Experimente você mesmo
Sobre as tabelas users e orders acima:
- Liste pares de nome do comprador e valor, ordenados por valor decrescente.
- Encontre todos os usuários que fizeram um pedido em março de 2024.
- Conte o valor total de pedidos por usuário (combinação
JOIN + GROUP BY + SUM).
Quando o JOIN faz sentido para você, é um momento libertador ao aprender SQL. Antes — você consegue ler uma tabela. Depois — os dados do mundo real se abrem por completo.
INNER JOIN(ou apenasJOIN) é o comando do SQL para combinar duas tabelas por uma chave compartilhada. O tipo de JOIN mais simples e mais comum — com bancos de dados relacionais você não vai longe sem ele.Uma analogia rápida. Você tem duas tabelas:
users— usuários (id, name)orders— pedidos (id, user_id, amount)Em
ordersnão há nome do comprador — apenasuser_id. Para obter pares de "nome do comprador + valor do pedido", você precisa colar essas tabelas de volta por uma chave comum —users.idcorresponde aorders.user_id. Essa colagem é oINNER JOIN.Por que JOIN importa
Um banco de dados normal distribui os dados entre tabelas para evitar a duplicação. O nome do usuário vive apenas em
users;ordersreferencia o usuário através deuser_id. Isso se chama normalização.Quando você precisa mostrar "nome + pedido", precisa montar os dados de volta — é para isso que serve o JOIN. É um dos temas mais importantes do SQL — sem JOIN você não consegue escrever nenhuma consulta do mundo real.
Sintaxe básica
SELECT columns FROM table_A INNER JOIN table_B ON table_A.key = table_B.key;A palavra
INNERé opcional —JOINsem prefixo significaINNER JOIN. A maioria do código escreve apenasJOIN.Exemplo: uma loja
Tabela
users:Tabela
orders:Consulta — juntar e mostrar o nome do comprador com o valor:
SELECT users.name, orders.amount, orders.created_at FROM users INNER JOIN orders ON users.id = orders.user_id;Resultado:
O que aconteceu:
usersfoi pareada com cada linha deordersondeusers.id = orders.user_id.user_id = 5não existe emusers. Às vezes acontece, por exemplo, o usuário foi excluído mas o pedido permaneceu.Se você também quiser os usuários sem pedidos, isso é
LEFT JOIN(artigo separado).Aliases — nomes curtos de tabela
Escrever
users.name,orders.amounttoda vez é longo. O código do mundo real usa aliases:SELECT u.name, o.amount, o.created_at FROM users u INNER JOIN orders o ON u.id = o.user_id;users usignifica "dê à tabelauserso aliasu". Não muda a lógica — apenas é mais curto. Em consultas grandes com três a cinco tabelas, as consultas sem aliases são ilegíveis.Observação: os aliases se tornam obrigatórios se ambas as tabelas têm uma coluna com o mesmo nome. Caso contrário o banco de dados não consegue saber a qual
idvocê se refere.Juntar três ou mais tabelas
Os JOINs se encadeiam: A JOIN B JOIN C. Adicione uma tabela
products:E estenda
orderscomproduct_id:Consulta — quem comprou o quê:
SELECT u.name AS buyer, p.name AS product, o.amount FROM users u INNER JOIN orders o ON u.id = o.user_id INNER JOIN products p ON p.id = o.product_id;Resultado:
Como funciona:
userseordersporuser_id.productsporproduct_id.Essas cadeias podem ser tão longas quanto você precisar. "Usuários, os seus pedidos, os produtos desses pedidos, as categorias dos produtos e as lojas" — isso são cinco JOINs seguidos.
JOIN com WHERE
JOINcostuma vir comWHERE— JOIN cola as tabelas, WHERE filtra o resultado. Por exemplo, os pedidos da Anna acima de 1000:SELECT u.name, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE u.name = 'Anna' AND o.amount > 1000;Uma combinação extremamente comum — quase qualquer consulta de negócio é "junte as tabelas + filtre com WHERE + talvez agregue".
INNER JOIN é a interseção
Em termos de teoria dos conjuntos: INNER JOIN é a interseção de duas tabelas por uma chave. Apenas as linhas presentes em ambas entram.
Um usuário sem pedidos — não está no resultado. Um pedido sem usuário (chave estrangeira quebrada) — também não está no resultado.
Exemplo maior: uma biblioteca
books:authors:Consulta — cada livro com o nome do autor e o país:
SELECT b.title, a.full_name AS author, a.country FROM books b JOIN authors a ON a.id = b.author_id ORDER BY a.full_name, b.title;Resultado:
O livro órfão sumiu —
author_id = 99não está emauthors. Remarque sumiu — ele tem zero livros na nossabooks. O INNER JOIN descartou os dois.Erros comuns de iniciante
1. Esquecer a cláusula ON. Escrever
JOIN orderssemON— a maioria dos bancos de dados ou dá erro ou recorre a um CROSS JOIN (produto cartesiano, cada linha vezes cada linha). Ou um erro ou uma consulta muito lenta.2. Colunas erradas no ON.
ON u.id = o.idem vez deON u.id = o.user_idé um erro de digitação clássico. A consulta não vai quebrar — vai retornar absurdos (o usuário #5 pareado com o pedido #5, o que não tem significado).3. Nomes de coluna ambíguos. Se tanto
usersquantoorderstêmid, e você escreveSELECT id, name FROM users JOIN orders ON …, você obtém "a referência à coluna "id" é ambígua". Useusers.idou aliases.4. Linhas duplicadas no resultado. Se um usuário tem 5 pedidos, o resultado tem 5 linhas com o nome desse usuário repetido. Não é um bug — é assim que o JOIN funciona. Quer usuários únicos? Envolva em
SELECT DISTINCT u.nameou combine comGROUP BY.5. Achar que o JOIN em si é lento. Os JOINs são rápidos quando há índices nas colunas de junção. É por isso que as colunas de chave estrangeira (
user_ide similares) são sempre indexadas em produção.6. Junções antigas com vírgula. Tecnicamente você pode escrever
FROM users, orders WHERE users.id = orders.user_id. Não faça isso. A sintaxe modernaJOIN ... ON ...se lê melhor e separa com clareza a junção da filtragem.Resumo rápido
INNER JOINcombina duas tabelas por uma chave compartilhada.ON.INNERé opcional; as pessoas escrevem apenasJOIN.users u) para manter as coisas legíveis.LEFT JOIN, um cenário diferente.Experimente você mesmo
Sobre as tabelas
userseordersacima:JOIN+GROUP BY+SUM).Quando o JOIN faz sentido para você, é um momento libertador ao aprender SQL. Antes — você consegue ler uma tabela. Depois — os dados do mundo real se abrem por completo.