SQL LEFT JOIN

O INNER JOIN só mostra os pares que combinam. Por isso, numa lista de clientes com os seus pedidos, os quatro clientes da Loja Aurora que nunca compraram (Paulo Sérgio, Tainá, Elias e Rosângela) simplesmente não aparecem. Mas a Ana precisa justamente deles: quer mandar um cupom de boas-vindas para quem se cadastrou e ainda não fez o primeiro pedido. Para isso existe o LEFT JOIN, a "junção pela esquerda": ele mostra todas as linhas da tabela da esquerda, tenham par ou não.

Você vai aprender
  • O que o LEFT JOIN faz com as linhas que não têm par: elas entram com NULL do outro lado.
  • Como achar só quem não tem par: clientes sem pedido e produtos nunca vendidos, com WHERE … IS NULL.
  • Como contar com zero, e por que COUNT(*) dá 1 para quem não tem nada.
  • Por que, no LEFT JOIN, um filtro no ON e no WHERE dão resultados diferentes.
  • O LEFT JOIN com três tabelas, e o JOIN comum que, colocado depois, apaga o efeito dele.
  • As outras formas de achar "quem não tem": NOT IN e NOT EXISTS.

Os exemplos rodam de verdade no seu navegador, no banco da Loja Aurora.


A ideia: a lista de chamada

Pense na lista de chamada de uma turma. A professora lê todos os nomes, um por um, e ao lado de cada um anota a nota da prova. Quem faltou à prova continua na lista, só que com o espaço da nota em branco. Ninguém some da lista por não ter feito a prova.

O LEFT JOIN é essa lista: a tabela da esquerda (a do FROM) é a lista de chamada, e todas as linhas dela aparecem. Ao lado, as colunas da tabela da direita, quando há par; quando não há, o espaço em branco, que em SQL é o NULL (aula O Valor NULL).

clientes pedidos
c.idc.nomep.id
26Mirela Pontes26
26Mirela Pontes52
26Mirela Pontes78
27Paulo Sérgio AndradeNULL
28Tainá SoaresNULL

1. O primeiro LEFT JOIN

Todos os clientes, cada um com os seus pedidos. São muitas linhas, então a Ana olha só os clientes a partir do 25:

SELECT c.id, c.nome, p.id AS pedido
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE c.id >= 25
ORDER BY c.id, p.id;
FROM clientes c
A tabela da esquerda: a "lista de chamada". Todas as linhas dela vão aparecer.
LEFT JOIN pedidos p
Left join = "junção pela esquerda". Pode escrever também LEFT OUTER JOIN ("junção externa pela esquerda"): é a mesma coisa.
ON p.cliente_id = c.id
A regra do par, igual à do INNER JOIN.
pedido = NULL
Para os clientes 27 a 30, nenhum pedido combinou. Eles entram uma vez, com todas as colunas de pedidos em NULL.

Em resumo, o LEFT JOIN faz o mesmo que o INNER JOIN e depois devolve ao resultado as linhas da esquerda que ficaram sem par. Na loja inteira, são os 90 pares pedido-cliente mais os 4 clientes sem pedido: 94 linhas.

SELECT COUNT(*)
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id;

2. Só quem não tem par: o IS NULL depois do LEFT JOIN

A Ana não quer a lista toda: quer só os clientes sem pedido, para o cupom. São exatamente as linhas em que o lado do pedido veio vazio. Basta filtrar por elas:

SELECT c.id, c.nome, c.email
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE p.id IS NULL;
LEFT JOIN pedidos p ON …
Primeiro, todos os clientes, com ou sem pedido.
WHERE p.id IS NULL
Depois, só as linhas em que p.id ficou vazio. Como todo pedido de verdade tem id (é a chave primária), um p.id vazio só pode significar "não houve par".

Os quatro: Paulo Sérgio Andrade, Tainá Soares, Elias Furtado e Rosângela Dias, com os emails para o cupom. Esse padrão, LEFT JOIN + WHERE chave-da-direita IS NULL, é um dos mais úteis do SQL: serve para qualquer pergunta do tipo "quem nunca…".

Experimente: a mesma pergunta, do outro lado do banco: quais produtos nunca foram vendidos? A consulta abaixo lista os produtos com os itens vendidos de cada um. Acrescente o filtro que deixa só os produtos sem nenhum item.

SELECT pr.id, pr.nome, i.pedido_id
FROM produtos pr
LEFT JOIN itens_pedido i ON i.produto_id = pr.id;
SELECT pr.id, pr.nome, i.pedido_id
FROM produtos pr
LEFT JOIN itens_pedido i ON i.produto_id = pr.id
WHERE i.produto_id IS NULL;

Devem sair os produtos 43, 44 e 45: o Vaso de Cerâmica Médio, a Tesoura de Poda e as Luvas de Jardinagem. Repare que a coluna testada foi i.produto_id, a própria coluna da ligação: ela nunca é vazia num item de verdade, então também serve.


3. Contando com zero

Quantos pedidos cada cliente fez, incluindo quem fez zero? A primeira tentativa da Ana:

SELECT c.nome, COUNT(*) AS pedidos
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE c.id >= 24
GROUP BY c.id, c.nome;

Errado! O Paulo Sérgio aparece com 1 pedido, mas ele nunca comprou. O COUNT(*) conta linhas, e o LEFT JOIN deu ao Paulo uma linha (aquela com o pedido em NULL). A correção é contar uma coluna do lado direito: COUNT(coluna) pula os NULL (aula COUNT()).

SELECT c.nome, COUNT(p.id) AS pedidos
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE c.id >= 24
GROUP BY c.id, c.nome;
EscritaPara o Paulo (sem pedido)Por quê
COUNT(*)1Conta a linha, mesmo com o lado direito vazio
COUNT(p.id)0Conta só os valores que não são NULL

Regra prática: com LEFT JOIN, conte uma coluna da tabela da direita, de preferência a chave primária dela. O mesmo vale para os produtos: quantas vezes cada produto de jardinagem (categoria 8) foi vendido?

SELECT pr.nome AS produto, COUNT(i.pedido_id) AS vendas
FROM produtos pr
LEFT JOIN itens_pedido i ON i.produto_id = pr.id
WHERE pr.categoria_id = 8
GROUP BY pr.id, pr.nome;

A categoria Jardim tem 5 produtos, e 3 deles nunca venderam. Não é à toa que ela foi a categoria com menos unidades vendidas na aula de INNER JOIN.


4. A ordem das tabelas importa

No INNER JOIN, tanto faz qual tabela vem primeiro. No LEFT JOIN, não: a da esquerda é a que fica inteira. Invertendo, com pedidos à esquerda:

SELECT COUNT(*)
FROM pedidos p
LEFT JOIN clientes c ON p.cliente_id = c.id;

90 linhas, como num INNER JOIN. Agora a "lista de chamada" é a dos pedidos, e todo pedido tem cliente (a chave estrangeira garante, como você viu em Tabelas que se Relacionam). Ninguém fica sem par, e os clientes sem pedido, que estão do lado direito, não têm vaga. Pergunte-se sempre: de qual tabela eu quero todas as linhas? Essa vai no FROM.


5. O filtro no ON ou no WHERE muda tudo

A Ana quer a lista de todos os clientes, com quantos pedidos cada um fez em 2026. Primeira tentativa, com o filtro da data no WHERE:

SELECT c.id, c.nome, COUNT(p.id) AS pedidos_2026
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE c.id >= 24
  AND p.data >= '2026-01-01'
GROUP BY c.id, c.nome;

Só 3 clientes: os 4 sem pedido sumiram, como se fosse um INNER JOIN. O que aconteceu? O WHERE roda depois do JOIN. Nas linhas dos clientes sem pedido, p.data é NULL, e NULL >= '2026-01-01' não é verdadeiro. O WHERE jogou essas linhas fora. Agora com o filtro da data dentro do ON:

SELECT c.id, c.nome, COUNT(p.id) AS pedidos_2026
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
                   AND p.data >= '2026-01-01'
WHERE c.id >= 24
GROUP BY c.id, c.nome;

Os 7 clientes, e os 4 sem pedido aparecem com 0. Dentro do ON, a data faz parte da regra do par: "combine o cliente com os pedidos dele de 2026". Quem não tem par com essa regra continua na lista, com NULL do lado direito, porque é isso que o LEFT JOIN garante.

Onde está o filtroO que ele faz num LEFT JOIN
No ONEscolhe quais linhas da direita podem formar par. As linhas da esquerda ficam todas.
No WHEREFiltra o resultado final. Um filtro numa coluna da direita apaga as linhas sem par (elas têm NULL ali), e o LEFT JOIN vira, na prática, um INNER JOIN.

Filtros sobre a tabela da esquerda (como c.id >= 24) vão no WHERE sem problema. A pegadinha é só com colunas da tabela da direita. A exceção é justamente o WHERE p.id IS NULL da seção 2: ali, você quer ficar só com as linhas sem par.


6. LEFT JOIN com três tabelas

Todos os clientes, com os pedidos e o vendedor de cada pedido. Clientes a partir do 26:

SELECT c.id, c.nome, p.id AS pedido, f.nome AS vendedor
FROM clientes c
LEFT JOIN pedidos p      ON p.cliente_id = c.id
LEFT JOIN funcionarios f ON p.funcionario_id = f.id
WHERE c.id >= 26
ORDER BY c.id, p.id;

Os dois JOINs são LEFT. O segundo também precisa ser: para os clientes sem pedido, p.funcionario_id é NULL, não há vendedor que combine, e só um LEFT JOIN mantém essas linhas. Veja o que acontece se o segundo for um JOIN comum:

SELECT c.id, c.nome, p.id AS pedido, f.nome AS vendedor
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
JOIN funcionarios f ON p.funcionario_id = f.id
WHERE c.id >= 26
ORDER BY c.id, p.id;

Os clientes 27 a 30 sumiram de novo. Os JOINs são processados da esquerda para a direita: o LEFT JOIN trouxe os clientes sem pedido, e logo depois o JOIN comum, que só guarda pares, os descartou. Regra: depois de um LEFT JOIN, as tabelas penduradas no lado direito também entram com LEFT JOIN.


Formas diferentes de achar "quem nunca…"

Os produtos nunca vendidos, de três jeitos que dão o mesmo resultado:

SELECT pr.id, pr.nome
FROM produtos pr
LEFT JOIN itens_pedido i ON i.produto_id = pr.id
WHERE i.produto_id IS NULL;
SELECT id, nome
FROM produtos
WHERE id NOT IN (SELECT produto_id FROM itens_pedido);
SELECT pr.id, pr.nome
FROM produtos pr
WHERE NOT EXISTS (SELECT 1 FROM itens_pedido i WHERE i.produto_id = pr.id);

As duas últimas usam uma subconsulta, uma consulta dentro da outra, assunto das aulas Subconsultas e EXISTS. Por enquanto, basta saber que elas existem e que dão o mesmo resultado.

FormaQuando usar
LEFT JOIN … WHERE direita.chave IS NULLQuando você também quer mostrar colunas das duas tabelas, ou já está montando um JOIN. Funciona em qualquer banco.
NOT IN (subconsulta)Leitura fácil ("o id não está na lista"). Cuidado: se a lista da subconsulta tiver algum NULL, o NOT IN não devolve nada (a aula de subconsultas explica).
NOT EXISTS (subconsulta)A forma mais segura com NULL; muito usada por quem já tem prática.

Quando dá errado

Quase nenhum erro de LEFT JOIN gera mensagem: a consulta roda e o resultado sai errado. Por isso vale conhecer cada armadilha.

Contou com COUNT(*) e quem não tem nada ficou com 1

Visto na seção 3: com LEFT JOIN, COUNT(*) conta a linha do NULL. Como consertar: COUNT(p.id), uma coluna da tabela da direita.

O filtro da direita no WHERE transformou o LEFT em INNER

Visto na seção 5: WHERE p.data >= '2026-01-01' apaga as linhas sem par. Como consertar: leve o filtro para o ON: LEFT JOIN pedidos p ON p.cliente_id = c.id AND p.data >= '2026-01-01'.

Testou o IS NULL numa coluna que pode ser vazia de verdade

A Ana quis os clientes sem pedido e testou a data de entrega em vez da chave:

SELECT c.id, c.nome
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE p.entregue_em IS NULL;

O que acontece: 9 clientes em vez de 4. Os 5 a mais (Fernanda, Antônio Carlos, Juliana, Marcos Vinícius e Patrícia) têm pedido: é que os pedidos 86 a 90 ainda não foram entregues, e a coluna entregue_em deles é NULL de verdade. O IS NULL não tem como saber se o vazio veio do LEFT JOIN ou da própria tabela.

Como consertar: teste uma coluna que nunca é vazia na tabela da direita: a chave primária (p.id) ou a coluna da ligação (p.cliente_id).

Escreveu = NULL em vez de IS NULL
SELECT c.id, c.nome
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE p.id = NULL;

O que acontece: nenhuma linha, e nenhum erro. Qualquer comparação com NULL usando = dá "desconhecido", nunca verdadeiro, nem quando o valor é NULL (aula IS NULL e IS NOT NULL).

Como consertar: WHERE p.id IS NULL.

Um JOIN comum depois do LEFT JOIN

Visto na seção 6: o JOIN seguinte descarta as linhas sem par que o LEFT JOIN tinha trazido. Como consertar: LEFT JOIN também nas tabelas que dependem do lado direito.

Esqueceu o ON
SELECT COUNT(*)
FROM clientes c
LEFT JOIN pedidos p;

O que acontece: 2.700 linhas, sem erro. Como no JOIN comum, o SQLite aceita o LEFT JOIN sem condição e junta cada cliente com cada pedido. Como consertar: LEFT JOIN pedidos p ON p.cliente_id = c.id.

Missão: a Ana queria só os clientes que nunca fizeram pedido, mas esta consulta devolve 9 clientes. Descubra o erro e conserte (devem sair 4).

SELECT c.id, c.nome
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE p.entregue_em IS NULL;
SELECT c.id, c.nome
FROM clientes c
LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE p.id IS NULL;
Nos outros bancos

LEFT JOIN e LEFT OUTER JOIN funcionam do mesmo jeito no MySQL, no PostgreSQL e no SQL Server, inclusive a diferença entre o filtro no ON e no WHERE: a documentação do SQL Server avisa que, nas junções externas, uma condição no ON e a mesma condição no WHERE podem dar resultados diferentes. A única diferença que você vai notar é o LEFT JOIN sem ON: pela gramática de cada um, o MySQL, o PostgreSQL e o SQL Server exigem a condição e dão erro, em vez de juntar tudo com tudo.


Referência rápida

EscritaO que faz
a LEFT JOIN b ON …Todas as linhas de a; as que não têm par em b vêm com NULL nas colunas de b
LEFT OUTER JOINO mesmo que LEFT JOIN
… WHERE b.chave IS NULLSó as linhas de a sem par ("quem nunca…")
COUNT(b.chave)Conta os pares; dá 0 para quem não tem par
LEFT JOIN b ON a.x = b.y AND b.z …Filtro que só limita quem pode ser par; as linhas de a ficam todas
a LEFT JOIN b … LEFT JOIN c …Cadeia de LEFT JOINs; um JOIN comum no meio desfaz o efeito
Sintoma (sem mensagem de erro)Causa provável
Quem não tem nada aparece com 1COUNT(*) em vez de COUNT(b.chave)
As linhas sem par sumiramFiltro numa coluna de b no WHERE, ou JOIN comum depois do LEFT JOIN
Vieram linhas a mais no "quem nunca…"IS NULL testado numa coluna que pode ser vazia de verdade
Não veio nenhuma linha= NULL em vez de IS NULL
Milhares de linhasFaltou o ON

Exercícios

1. Quem nunca comprou

Complete para listar só os clientes sem pedido:

SELECT c.nome FROM clientes c pedidos p ON p.cliente_id = c.id WHERE p.id ;

2. Contando certo

Complete para que os clientes sem pedido apareçam com 0:

SELECT c.nome, COUNT() FROM clientes c LEFT JOIN pedidos p ON p.cliente_id = c.id GROUP BY c.id, c.nome;

3. SELECT COUNT(*) FROM clientes c LEFT JOIN pedidos p ON p.cliente_id = c.id; devolve 94. De onde vêm as 4 linhas a mais que os 90 pedidos?

4. A Ana quer todos os clientes, com os pedidos de 2026. Onde deve ir p.data >= '2026-01-01'?

5. FROM pedidos p LEFT JOIN clientes c ON p.cliente_id = c.id mostra os clientes que nunca compraram?

Missão final: todos os funcionários

Missão: na aula de INNER JOIN, a lista de pedidos por vendedor mostrou só 6 pessoas. A gerente quer ver os 8 funcionários, inclusive quem não atendeu nenhum pedido, com 0. Troque o tipo de JOIN e conserte a contagem.

SELECT f.nome AS funcionario, COUNT(*) AS pedidos
FROM funcionarios f
JOIN pedidos p ON p.funcionario_id = f.id
GROUP BY f.id, f.nome;
SELECT f.nome AS funcionario, COUNT(p.id) AS pedidos
FROM funcionarios f
LEFT JOIN pedidos p ON p.funcionario_id = f.id
GROUP BY f.id, f.nome;

A gerente Marta Lustosa e o estoquista Davi Nunes aparecem com 0. Se você trocou só o JOIN e deixou COUNT(*), eles saíram com 1: é a armadilha da seção 3. Para mostrar um texto como "sem pedidos" no lugar de um NULL, existe o COALESCE, da aula IFNULL e COALESCE.


Resumo

  • LEFT JOIN mostra todas as linhas da tabela da esquerda; as que não têm par entram com NULL nas colunas da direita.
  • LEFT JOIN … WHERE direita.chave IS NULL encontra quem não tem par: clientes sem pedido (27 a 30), produtos nunca vendidos (43 a 45). Teste a chave, nunca uma coluna que pode ser vazia de verdade.
  • Para contar com zero, use COUNT(direita.chave). COUNT(*) dá 1 para quem não tem nada.
  • A ordem importa: a tabela de que você quer todas as linhas vai no FROM.
  • Filtro numa coluna da direita: no ON, limita os pares; no WHERE, apaga as linhas sem par e transforma o LEFT em INNER.
  • Depois de um LEFT JOIN, use LEFT JOIN também nas tabelas penduradas no lado direito.