[A Muralha do Aprendiz de SQL] Conquiste os JOINs! Qual é a diferença entre INNER JOIN e LEFT JOIN?
"Tenho uma lista de funcionários, mas os nomes dos departamentos estão em outro arquivo... Quero ver uma lista com o nome do funcionário e o nome do seu departamento juntos!"
"Quero uma lista que mostre de relance qual produto pertence a qual categoria!"
Como um criador web que trabalha com bancos de dados, um desafio que você quase certamente enfrentará é "querer ver informações espalhadas por várias tabelas de forma consolidada". Informações de clientes e histórico de compras, mestre de produtos e tabela de estoque... em um banco de dados normalizado, é comum que as informações sejam divididas em tabelas por finalidade.
A poderosa arma do SQL que conecta essas informações dispersas, como se estivesse montando as peças de um quebra-cabeça, é o "JOIN". Se você dominar os JOINs, a gama de dados que você pode manipular com SQL se expandirá explosivamente, permitindo que você extraia informações mais complexas e valiosas.
Neste artigo, vamos nos concentrar em dois dos JOINs mais frequentemente usados, INNER JOIN e LEFT JOIN, e explicar detalhadamente suas funções e como escolher entre eles, com diagramas e código pronto para copiar e colar que até mesmo os iniciantes entenderão com certeza!
Preparação: Tabelas para Aprender a Usar JOIN
Para entender claramente como os JOINs funcionam, desta vez também usaremos uma tabela de "funcionários (employees)" e uma de "departamentos (departments)". Para tornar as diferenças entre os JOINs fáceis de ver, incluímos deliberadamente dados para "um novo estagiário ainda não atribuído a um departamento" e "um novo departamento de Relações Públicas sem nenhum funcionário".
Por favor, copie a instrução SQL abaixo e execute-a em seu próprio ambiente de banco de dados, ou experimente-a no "Ambiente de Execução de SQL" descrito mais adiante.
-- Excluir tabelas se existirem
DROP TABLE IF EXISTS employees;
DROP TABLE IF EXISTS departments;
-- Criar a tabela de departamentos
CREATE TABLE departments (
id INT PRIMARY KEY,
department_name VARCHAR(50)
);
-- Inserir dados dos departamentos
INSERT INTO departments (id, department_name) VALUES
(1, '営業部'),
(2, '開発部'),
(3, '人事部'),
(4, '広報部'); -- Um departamento ainda sem funcionários
-- Criar a tabela de funcionários
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
department_id INT -- Permitir NULL
);
-- Inserir dados dos funcionários
INSERT INTO employees (id, name, department_id) VALUES
(1, '山田 太郎', 1),
(2, '鈴木 花子', 2),
(3, '佐藤 次郎', 1),
(4, '高橋 三郎', 3),
(5, '田中 恵子', 2),
(6, '中村 さくら', NULL); -- Um novo estagiário ainda não atribuído a um departamento
Pronto? Vamos embarcar na jornada para conectar estas duas tabelas!
1. INNER JOIN: Juntando Apenas as Partes Comuns de Duas Tabelas
INNER JOIN é o tipo de JOIN mais básico e mais comumente usado. Em poucas palavras, é um método de junção para "extrair apenas os dados relacionados que existem em ambas as tabelas".
Se você o visualizar com um diagrama de Venn, é como pegar apenas a "interseção" onde os dois círculos se sobrepõem. Em outras palavras, você o usaria em casos em que deseja apenas informações sobre "funcionários que pertencem a um departamento".
Uso Básico
Você especifica a primeira tabela na cláusula FROM e a segunda tabela com INNER JOIN. Em seguida, na cláusula ON, você define qual coluna usar como chave para vincular as tabelas.
SELECT
e.name,
d.department_name
FROM
employees AS e
INNER JOIN
departments AS d ON e.department_id = d.id;
Dê uma olhada neste resultado. Existem dois pontos importantes:
- 'Nakamura Sakura', que не está atribuída a um departamento, não está incluída nos resultados.
- O 'Departamento de Relações Públicas' (広報部), que não tem funcionários, também não está incluído.
Esta é a principal característica de um INNER JOIN. Apenas os dados que satisfazem a condição de junção ON e.department_id = d.id — ou seja, dados que existem em ambas as tabelas — são retornados.
※ Ao definir um alias após o nome da tabela, como employees AS e, você pode escrever consultas mais curtas e convenientes!
2. LEFT JOIN: Exibindo Todos os Registros de Uma Tabela
Um LEFT JOIN é um método de junção que "exibe todos os dados da tabela da esquerda (a primeira especificada na cláusula FROM) e anexa os dados relacionados da tabela da direita". Também é chamado de LEFT OUTER JOIN, mas a palavra-chave `OUTER` é opcional.
Em um diagrama de Venn, é como incluir todo o círculo esquerdo e apenas a parte sobreposta do círculo direito. Se não houver dados correspondentes na tabela da direita, essa parte conterá NULL (vazio).
Exemplo 1: Exibindo todos os dados com base nos "Funcionários"
Vamos considerar o caso em que "quero uma lista completa de funcionários. Se eles pertencerem a um departamento, também quero saber o nome do departamento". Neste caso, colocamos a tabela "employees", que é a nossa base, no lado esquerdo.
SELECT
e.name,
d.department_name
FROM
employees AS e
LEFT JOIN
departments AS d ON e.department_id = d.id;
Preste atenção ao resultado! Ao contrário do INNER JOIN, 'Nakamura Sakura', que não tem um departamento atribuído, está incluída no resultado. E o seu department_name é NULL.
Este é o poder do LEFT JOIN. Como todos os dados da tabela da esquerda (employees) são exibidos, é perfeito para tarefas como "encontrar funcionários que ainda não foram atribuídos a um departamento".
Exemplo 2: Exibindo todos os dados com base nos "Departamentos"
Então, o que acontece se trocarmos as tabelas da esquerda e da direita? Vamos considerar o caso em que "quero ver uma lista de todos os departamentos. Se tiverem funcionários, quero saber os nomes deles também". Desta vez, vamos colocar a tabela "departments" à esquerda.
SELECT
d.department_name,
e.name
FROM
departments AS d
LEFT JOIN
employees AS e ON d.id = e.department_id;
O resultado é diferente novamente! O 'Departamento de Relações Públicas' (広報部), que não tem funcionários, agora é exibido corretamente, e o nome do funcionário correspondente (name) é NULL. Como você pode ver, com um LEFT JOIN, qual tabela você coloca no lado "esquerdo" é extremamente importante.
Pontos-Chave para Escolher entre INNER JOIN e LEFT JOIN
Se você alguma vez ficar em dúvida sobre qual usar, apenas pense no que você quer que seja o "protagonista" dos seus dados.
-
INNER JOIN: "Eu só quero ver dados onde a informação do funcionário e do departamento esteja completa!"
→ Use quando quiser ver a relação estrita entre duas tabelas. -
LEFT JOIN: "Eu quero ver todos os funcionários. A informação do departamento é opcional!"
→ Use quando quiser exibir todos os registros de uma tabela "mestre" como sua base.
Se você pensar com base neste critério, será capaz de decidir naturalmente qual JOIN é o apropriado.
Prática! Vamos Experimentar os JOINs no seu Navegador
Obrigado por esperar! Aqui está um ambiente onde você pode colocar a mão na massa e experimentar o INNER JOIN e o LEFT JOIN que aprendeu até agora.
Copie o bloco de código inteiro abaixo, salve-o como um arquivo com um nome como join_practice.html e abra-o no seu navegador. Tente trocar as tabelas da esquerda e da direita, ou alternar entre INNER e LEFT, para ver como os resultados mudam!
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
<title>Ambiente de Testes para SQL JOIN</title>
<script src="https://cdn.jsdelivr.net/npm/alasql@4"></script>
<style>
body { font-family: sans-serif; padding: 2rem; background-color: #f9f9f9; }
.container { max-width: 800px; margin: auto; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
h1 { color: #333; }
textarea { width: 100%; height: 180px; font-family: monospace; font-size: 16px; padding: 10px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; margin-bottom: 1rem; }
button { background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
button:hover { background-color: #0056b3; }
#result-area { margin-top: 2rem; }
table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.error { color: red; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<h1>Vamos Experimentar os JOINs!</h1>
<p>Insira uma instrução SQL na área de texto abaixo e pressione o botão "Executar SQL".</p>
<textarea id="sql-input">-- Escreva seu SQL aqui
SELECT
e.name,
d.department_name
FROM
employees AS e
LEFT JOIN
departments AS d ON e.department_id = d.id;</textarea>
<button onclick="executeSQL()">Executar SQL</button>
<div id="result-area"></div>
</div>
<script>
// Inicializar o banco de dados e preparar os dados
const db = new alasql.Database();
db.exec(`
CREATE TABLE departments (id INT, department_name STRING);
INSERT INTO departments VALUES (1, '営業部'), (2, '開発部'), (3, '人事部'), (4, '広報部');
CREATE TABLE employees (id INT, name STRING, department_id INT);
INSERT INTO employees VALUES (1, '山田 太郎', 1), (2, '鈴木 花子', 2), (3, '佐藤 次郎', 1), (4, '高橋 三郎', 3), (5, '田中 恵子', 2), (6, '中村 さくら', NULL);
`);
function executeSQL() {
const sql = document.getElementById('sql-input').value;
const resultArea = document.getElementById('result-area');
resultArea.innerHTML = '';
try {
const result = db.exec(sql);
if (result.length > 0) {
resultArea.appendChild(createTable(result));
} else {
resultArea.innerHTML = '<p>A consulta retornou 0 linhas.</p>';
}
} catch (e) {
resultArea.innerHTML = `<p class="error">Erro: ${e.message}</p>`;
}
}
function createTable(data) {
const table = document.createElement('table');
const thead = table.createTHead();
const tbody = table.createTBody();
const headerRow = thead.insertRow();
for (const key in data[0]) {
const th = document.createElement('th');
th.textContent = key;
headerRow.appendChild(th);
}
data.forEach(rowData => {
const row = tbody.insertRow();
for (const key in rowData) {
const cell = row.insertCell();
cell.textContent = (rowData[key] === null || rowData[key] === undefined) ? 'NULL' : rowData[key];
}
});
return table;
}
// Exibição inicial
executeSQL();
</script>
</body>
</html>
Pontos de Atenção e Técnicas Relacionadas
A Diferença Entre ON e WHERE
Os iniciantes muitas vezes se confundem sobre quando usar ON em vez de WHERE. Vamos lembrar de forma simples:
- ON: Define a regra de junção — "qual chave usar" para conectar as tabelas.
- WHERE: Define a regra de filtragem — "quais linhas selecionar" da grande tabela combinada que o join criou.
Pense nisso como um fluxo: primeiro, você conecta corretamente as tabelas com ON, e depois filtra apenas os dados necessários com WHERE.
RIGHT JOIN e FULL OUTER JOIN
O JOIN também tem outros membros na família.
- RIGHT JOIN: É a versão inversa do
LEFT JOIN. Exibe todos os registros da tabela da direita como base. - FULL OUTER JOIN: Exibe todos os dados de ambas as tabelas, esquerda e direita, inserindo
NULLonde não há dados correspondentes.
Na prática, você usará o LEFT JOIN com uma frequência esmagadora, mas é bom ter em um canto da sua mente que estes outros membros existem.
Resumo
Ótimo trabalho! Explicamos as diferenças e os casos de uso de INNER JOIN e LEFT JOIN, os fundamentos da junção de tabelas.
- INNER JOIN: Use quando quiser apenas as partes comuns de duas tabelas. Dados não relacionados são excluídos do resultado.
- LEFT JOIN: Use quando quiser exibir todos os dados com uma tabela como protagonista. Dados relacionados ausentes são complementados com
NULL. - A escolha de qual JOIN usar é decidida por "O que eu quero que seja o protagonista dos meus dados?"!
JOIN é um recurso fundamental para liberar o verdadeiro potencial dos bancos de dados relacionais. Pode parecer um pouco difícil no início, mas quanto mais você o usa, mais entenderá sua conveniência e mais divertido se tornará extrair dados. Por favor, experimente vários padrões e faça do JOIN sua nova arma!