LEFT JOIN (nome completo LEFT OUTER JOIN) é uma junção em que todas as linhas da tabela esquerda acabam no resultado. Quando não há correspondência à direita, as colunas do lado direito ficam NULL.
Compare com o INNER JOIN: aquele descarta as linhas sem correspondência. LEFT JOIN as mantém, apenas marca "nada à direita".
Analogia. Uma professora distribui provas. Nem todo mundo entregou a sua.
INNER JOIN — "me mostre apenas os alunos que entregaram uma prova, com a nota deles".
LEFT JOIN — "me mostre todos os alunos; a nota se entregaram, em branco se não".
A segunda é mais frequentemente o que você precisa na vida real — você quer ver quem está ficando para trás.
Por que LEFT JOIN importa
Cenários clássicos:
- Uma lista mais informação opcional, mesmo que falte. Todos os usuários + quantos pedidos têm (inclusive 0).
- Encontrar órfãos — linhas sem correspondência. Usuários sem pedidos. Produtos que ninguém nunca comprou. Publicações sem comentários.
- Contar lacunas nos dados. Quantas aulas não têm lição de casa, quantas publicações não têm comentários.
INNER JOIN não serve aqui — descarta as linhas "sem filhos" na hora.
Sintaxe básica
SELECT columns
FROM table_A
LEFT JOIN table_B
ON A.key = B.key;
"Esquerda" é o que vem depois de FROM. "Direita" é o que vem depois de LEFT JOIN. Lembre-se da direção: LEFT significa que o lado esquerdo é totalmente preservado.
OUTER é opcional. LEFT JOIN e LEFT OUTER JOIN são a mesma coisa.
Exemplo: uma sala de aula
students:
| id | name |
|---|
| 1 | Anna |
| 2 | Boris |
| 3 | Vera |
| 4 | Grisha |
grades (notas de prova):
| id | student_id | score |
|---|
| 10 | 1 | 5 |
| 11 | 1 | 4 |
| 12 | 2 | 3 |
Consulta: "cada aluno com as suas notas, se houver".
SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id;
Resultado:
| name | score |
|---|
| Anna | 5 |
| Anna | 4 |
| Boris | 3 |
| Vera | NULL |
| Grisha | NULL |
O que observar:
- Os quatro alunos aparecem no resultado. Esse é todo o sentido do LEFT JOIN.
- Anna tem duas notas — duas linhas.
- Boris tem uma — uma linha.
- Vera e Grisha não têm notas → uma linha cada um, com
NULL em score.
Se tivéssemos escrito INNER JOIN, só restariam Anna (duas linhas) e Boris. Vera e Grisha cairiam — justamente as pessoas com quem a professora se importa.
Encontrar órfãos — linhas sem correspondência
Truque clássico. Para encontrar os alunos que não fizeram nenhuma prova, filtre por IS NULL:
SELECT s.name
FROM students s
LEFT JOIN grades g ON s.id = g.student_id
WHERE g.id IS NULL;
Resultado:
Lógica: LEFT JOIN mantém cada aluno. Os que não têm notas têm NULL em g.id. WHERE g.id IS NULL fica apenas com eles.
Esse padrão (LEFT JOIN ... WHERE other.id IS NULL) é a forma padrão de encontrar "sem correspondência". É usado por toda parte: usuários sem pedidos, publicações sem comentários, produtos que ninguém comprou.
Não filtre a tabela direita no WHERE
A armadilha. Se você quer apenas as notas 5 e escreve:
SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id
WHERE g.score = 5;
Resultado:
Vera e Grisha caíram. Porque para eles WHERE g.score = 5 é avaliado como NULL = 5 (não TRUE) e eles são filtrados. O LEFT JOIN virou de fato um INNER JOIN.
Se você quer manter todos os alunos mas mostrar a nota apenas quando for um 5 — a condição vai no ON, não no WHERE:
SELECT s.name, g.score
FROM students s
LEFT JOIN grades g ON s.id = g.student_id AND g.score = 5;
Resultado:
| name | score |
|---|
| Anna | 5 |
| Boris | NULL |
| Vera | NULL |
| Grisha | NULL |
Agora os quatro estão no resultado; a nota aparece apenas quando é um 5. Uma diferença sutil mas crítica. Lembre-se: as condições sobre a tabela direita → ON; sobre a esquerda → WHERE.
INNER versus LEFT — visualizado
Imagine duas tabelas de um time de futebol: players e goals (gols marcados). Nem todo jogador marcou.
INNER JOIN → apenas os jogadores que marcaram pelo menos uma vez.
LEFT JOIN de players para goals → todos os jogadores; os que não marcaram têm NULL nas colunas de gols.
LEFT JOIN + WHERE goals.id IS NULL → apenas os jogadores que não marcaram de jeito nenhum.
Três perguntas diferentes — três consultas diferentes.
Exemplo maior: e-commerce
users:
| id | name | signup_date |
|---|
| 1 | Anna | 2024-01-15 |
| 2 | Boris | 2024-02-01 |
| 3 | Vera | 2024-02-20 |
| 4 | Grisha | 2024-03-10 |
orders:
| id | user_id | amount | created_at |
|---|
| 50 | 1 | 500 | 2024-02-05 |
| 51 | 1 | 1500 | 2024-02-10 |
| 52 | 2 | 200 | 2024-02-25 |
Pedido de negócio: "quantos pedidos e quanto gastou no total cada usuário cadastrado? Inclua os que ainda não compraram nada".
SELECT
u.name,
COUNT(o.id) AS orders_count,
COALESCE(SUM(o.amount), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY total_spent DESC;
Resultado:
| name | orders_count | total_spent |
|---|
| Anna | 2 | 2000 |
| Boris | 1 | 200 |
| Vera | 0 | 0 |
| Grisha | 0 | 0 |
Pontos-chave:
- Aparecem os 4 usuários, inclusive Vera e Grisha com zero pedidos.
COUNT(o.id) retorna 0 para eles (NULL não conta no COUNT).
SUM retornaria NULL para eles, então o envolvemos em COALESCE(..., 0) — transformar NULL em 0 para uma saída limpa.
Uma consulta de painel do pão de cada dia: "atividade por usuário, inclusive o zero".
Erros comuns de iniciante
1. WHERE sobre a tabela direita, transformando sem querer LEFT em INNER. O erro mais comum e mais traiçoeiro. Os filtros sobre colunas do lado direito → ON. Os filtros sobre colunas do lado esquerdo → WHERE.
2. Assustar-se com os NULL no resultado. Não são um problema — são um sinal de "sem correspondência à direita". Use IS NULL, COALESCE, agregações que ignoram NULL, e você está bem.
3. Usar LEFT JOIN quando é preciso INNER. Se o negócio diz "apenas os usuários que TÊM pedidos" — isso é INNER JOIN. LEFT JOIN aqui dá linhas a mais e pode deixar a consulta mais lenta.
4. Empilhar vários LEFT JOIN e perder o fio. As cadeias de A LEFT JOIN B LEFT JOIN C são delicadas — cada nova condição é relativa ao que já existe. Para junções complexas, construa-as de forma incremental.
5. RIGHT JOIN em vez de trocar as tabelas. RIGHT JOIN existe e faz a imagem espelhada. Na prática quase ninguém o usa — simplesmente troque as tabelas e fique com LEFT JOIN. Mais fácil de ler.
6. COUNT(*) versus COUNT(column) depois de um LEFT JOIN. COUNT(*) conta todas as linhas, inclusive aquelas em que o lado direito é NULL. COUNT(o.id) conta apenas as correspondências reais. Com LEFT JOIN esses dão números diferentes.
Resumo rápido
LEFT JOIN mantém TODAS as linhas da tabela esquerda.
- Se o lado direito não tem correspondência — as suas colunas ficam
NULL.
LEFT JOIN ... WHERE other.id IS NULL é a forma padrão de encontrar as linhas sem correspondência.
- As condições sobre o lado direito vão no
ON; sobre o lado esquerdo, no WHERE.
OUTER é opcional; LEFT JOIN = LEFT OUTER JOIN.
- Mais comum nas tarefas do mundo real do que INNER, porque você normalmente quer ver o quadro completo, inclusive os zeros.
Experimente você mesmo
Sobre users e orders acima:
- Liste TODOS os usuários com o número de pedidos que têm (inclusive o zero).
- Encontre os usuários sem nenhum pedido (padrão
IS NULL).
- Liste TODOS os usuários com o seu pedido mais recente (se houver). Dica:
LEFT JOIN + MAX(created_at) + GROUP BY.
Quando o LEFT JOIN faz sentido, abre-se toda uma classe de problemas que simplesmente não dá para resolver sem ele.
LEFT JOIN(nome completoLEFT OUTER JOIN) é uma junção em que todas as linhas da tabela esquerda acabam no resultado. Quando não há correspondência à direita, as colunas do lado direito ficamNULL.Compare com o
INNER JOIN: aquele descarta as linhas sem correspondência.LEFT JOINas mantém, apenas marca "nada à direita".Analogia. Uma professora distribui provas. Nem todo mundo entregou a sua.
INNER JOIN— "me mostre apenas os alunos que entregaram uma prova, com a nota deles".LEFT JOIN— "me mostre todos os alunos; a nota se entregaram, em branco se não".A segunda é mais frequentemente o que você precisa na vida real — você quer ver quem está ficando para trás.
Por que LEFT JOIN importa
Cenários clássicos:
INNER JOINnão serve aqui — descarta as linhas "sem filhos" na hora.Sintaxe básica
SELECT columns FROM table_A -- esquerda LEFT JOIN table_B -- direita ON A.key = B.key;"Esquerda" é o que vem depois de
FROM. "Direita" é o que vem depois deLEFT JOIN. Lembre-se da direção: LEFT significa que o lado esquerdo é totalmente preservado.OUTERé opcional.LEFT JOINeLEFT OUTER JOINsão a mesma coisa.Exemplo: uma sala de aula
students:grades(notas de prova):Consulta: "cada aluno com as suas notas, se houver".
SELECT s.name, g.score FROM students s LEFT JOIN grades g ON s.id = g.student_id;Resultado:
O que observar:
NULLemscore.Se tivéssemos escrito
INNER JOIN, só restariam Anna (duas linhas) e Boris. Vera e Grisha cairiam — justamente as pessoas com quem a professora se importa.Encontrar órfãos — linhas sem correspondência
Truque clássico. Para encontrar os alunos que não fizeram nenhuma prova, filtre por
IS NULL:SELECT s.name FROM students s LEFT JOIN grades g ON s.id = g.student_id WHERE g.id IS NULL;Resultado:
Lógica:
LEFT JOINmantém cada aluno. Os que não têm notas têmNULLemg.id.WHERE g.id IS NULLfica apenas com eles.Esse padrão (
LEFT JOIN ... WHERE other.id IS NULL) é a forma padrão de encontrar "sem correspondência". É usado por toda parte: usuários sem pedidos, publicações sem comentários, produtos que ninguém comprou.Não filtre a tabela direita no WHERE
A armadilha. Se você quer apenas as notas 5 e escreve:
SELECT s.name, g.score FROM students s LEFT JOIN grades g ON s.id = g.student_id WHERE g.score = 5;Resultado:
Vera e Grisha caíram. Porque para eles
WHERE g.score = 5é avaliado comoNULL = 5(não TRUE) e eles são filtrados. O LEFT JOIN virou de fato um INNER JOIN.Se você quer manter todos os alunos mas mostrar a nota apenas quando for um 5 — a condição vai no ON, não no WHERE:
SELECT s.name, g.score FROM students s LEFT JOIN grades g ON s.id = g.student_id AND g.score = 5;Resultado:
Agora os quatro estão no resultado; a nota aparece apenas quando é um 5. Uma diferença sutil mas crítica. Lembre-se: as condições sobre a tabela direita →
ON; sobre a esquerda →WHERE.INNER versus LEFT — visualizado
Imagine duas tabelas de um time de futebol:
playersegoals(gols marcados). Nem todo jogador marcou.INNER JOIN→ apenas os jogadores que marcaram pelo menos uma vez.LEFT JOINdeplayersparagoals→ todos os jogadores; os que não marcaram têmNULLnas colunas de gols.LEFT JOIN+WHERE goals.id IS NULL→ apenas os jogadores que não marcaram de jeito nenhum.Três perguntas diferentes — três consultas diferentes.
Exemplo maior: e-commerce
users:orders:Pedido de negócio: "quantos pedidos e quanto gastou no total cada usuário cadastrado? Inclua os que ainda não compraram nada".
SELECT u.name, COUNT(o.id) AS orders_count, COALESCE(SUM(o.amount), 0) AS total_spent FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id, u.name ORDER BY total_spent DESC;Resultado:
Pontos-chave:
COUNT(o.id)retorna 0 para eles (NULL não conta no COUNT).SUMretornariaNULLpara eles, então o envolvemos emCOALESCE(..., 0)— transformar NULL em 0 para uma saída limpa.Uma consulta de painel do pão de cada dia: "atividade por usuário, inclusive o zero".
Erros comuns de iniciante
1. WHERE sobre a tabela direita, transformando sem querer LEFT em INNER. O erro mais comum e mais traiçoeiro. Os filtros sobre colunas do lado direito →
ON. Os filtros sobre colunas do lado esquerdo →WHERE.2. Assustar-se com os NULL no resultado. Não são um problema — são um sinal de "sem correspondência à direita". Use
IS NULL,COALESCE, agregações que ignoram NULL, e você está bem.3. Usar LEFT JOIN quando é preciso INNER. Se o negócio diz "apenas os usuários que TÊM pedidos" — isso é
INNER JOIN.LEFT JOINaqui dá linhas a mais e pode deixar a consulta mais lenta.4. Empilhar vários LEFT JOIN e perder o fio. As cadeias de
A LEFT JOIN B LEFT JOIN Csão delicadas — cada nova condição é relativa ao que já existe. Para junções complexas, construa-as de forma incremental.5. RIGHT JOIN em vez de trocar as tabelas.
RIGHT JOINexiste e faz a imagem espelhada. Na prática quase ninguém o usa — simplesmente troque as tabelas e fique comLEFT JOIN. Mais fácil de ler.6. COUNT(*) versus COUNT(column) depois de um LEFT JOIN.
COUNT(*)conta todas as linhas, inclusive aquelas em que o lado direito é NULL.COUNT(o.id)conta apenas as correspondências reais. Com LEFT JOIN esses dão números diferentes.Resumo rápido
LEFT JOINmantém TODAS as linhas da tabela esquerda.NULL.LEFT JOIN ... WHERE other.id IS NULLé a forma padrão de encontrar as linhas sem correspondência.ON; sobre o lado esquerdo, noWHERE.OUTERé opcional;LEFT JOIN=LEFT OUTER JOIN.Experimente você mesmo
Sobre
userseordersacima:IS NULL).LEFT JOIN+MAX(created_at)+GROUP BY.Quando o LEFT JOIN faz sentido, abre-se toda uma classe de problemas que simplesmente não dá para resolver sem ele.