Mantenha o SQLtest vivo: Ajude a cobrir os custos do servidor de $100 deste mês para manter todas as lições e o playground SQL online. Apoie o projeto →
Código SQL copiado para a área de transferência

Lição 5.3 · Tempo de leitura: ~7 min

O SQL LEFT JOIN devolve todas as linhas da tabela da esquerda e as linhas correspondentes da tabela da direita; quando não há correspondência, as colunas da tabela da direita ficam NULL. Nesta lição, você vai aprender a sintaxe do LEFT JOIN, como encontrar linhas sem correspondência e o erro no WHERE que transforma silenciosamente um LEFT JOIN num INNER JOIN.

SQL LEFT JOIN: Manter Todas as Linhas da Tabela da Esquerda

O INNER JOIN descarta as linhas sem correspondência. Muitas vezes, são exatamente essas as linhas de que você precisa: filmes sem cópias em estoque, clientes sem pedidos este mês, produtos que nunca foram vendidos. O LEFT JOIN mantém-nas.

SQL LEFT JOIN: todas as linhas da tabela da esquerda são mantidas, as linhas correspondentes da tabela da direita são acrescentadas, e as linhas sem correspondência recebem NULL nas colunas da direita

O Que o LEFT JOIN Devolve

  • Cada linha da tabela da esquerda (a que vem depois de FROM) aparece no resultado pelo menos uma vez.
  • Se uma linha tem correspondências na tabela da direita, é combinada com cada uma delas, como no INNER JOIN.
  • Se uma linha não tem correspondência, aparece uma vez, e todas as colunas da tabela da direita ficam NULL.

LEFT JOIN e LEFT OUTER JOIN são a mesma coisa; a palavra OUTER é opcional.

Sintaxe Básica

SELECT left_table.column1, right_table.column2
FROM left_table
LEFT JOIN right_table ON left_table.key_column = right_table.key_column;

Filmes e as Suas Cópias

O Sakila tem 1000 filmes, mas nem todos estão em estoque. Com LEFT JOIN, os filmes sem cópias permanecem no resultado:

SELECT f.film_id, f.title, i.inventory_id, i.store_id
FROM film AS f
LEFT JOIN inventory AS i ON i.film_id = f.film_id
ORDER BY i.inventory_id IS NULL DESC, f.title
LIMIT 10;

Resultado: os filmes sem cópias vêm primeiro; o inventory_id e o store_id deles são NULL.

Com INNER JOIN, a mesma consulta perderia esses 42 filmes.


Encontrar Linhas Sem Correspondência

Para encontrar apenas as linhas sem correspondência, adicione uma condição WHERE que verifique NULL numa coluna da tabela da direita. Verifique a coluna-chave da tabela da direita: ela só é NULL quando nenhuma linha correspondeu.

SELECT f.film_id, f.title
FROM film AS f
LEFT JOIN inventory AS i ON i.film_id = f.film_id
WHERE i.inventory_id IS NULL
ORDER BY f.title;

Resultado: os 42 filmes que não têm cópias em nenhuma loja.

Esse padrão chama-se anti-join. Ele responde a perguntas do tipo "o que está faltando". Outro exemplo, cópias que nunca foram alugadas:

SELECT i.inventory_id, f.title, i.store_id
FROM inventory AS i
INNER JOIN film AS f ON f.film_id = i.film_id
LEFT JOIN rental AS r ON r.inventory_id = i.inventory_id
WHERE r.rental_id IS NULL;

Resultado: uma cópia que nunca foi alugada.


A Armadilha do WHERE

Um filtro sobre a tabela da direita colocado no WHERE remove as linhas em que as colunas dessa tabela são NULL, e o LEFT JOIN passa a comportar-se como um INNER JOIN. Compare duas consultas: ambas deveriam listar todos os clientes com os seus pagamentos feitos a partir de 23 de agosto de 2005.

-- Errado: clientes sem esses pagamentos desaparecem
SELECT c.customer_id, c.last_name, p.amount, p.payment_date
FROM customer AS c
LEFT JOIN payment AS p ON p.customer_id = c.customer_id
WHERE p.payment_date >= '2005-08-23'
ORDER BY c.customer_id;
-- Certo: a condição faz parte da junção
SELECT c.customer_id, c.last_name, p.amount, p.payment_date
FROM customer AS c
LEFT JOIN payment AS p ON p.customer_id = c.customer_id
                      AND p.payment_date >= '2005-08-23'
ORDER BY c.customer_id;

Resultado: a primeira consulta devolve apenas os clientes que pagaram nesse período. A segunda mantém todos os 599 clientes; os que não pagaram no fim de agosto têm NULL em amount e payment_date.

A regra: condições sobre a tabela da direita que devem limitar as correspondências vão para o ON. Condições sobre a tabela da esquerda, que devem remover linhas do resultado, vão para o WHERE. A única condição sobre a tabela da direita que pertence ao WHERE é IS NULL, num anti-join.


A Ordem das Tabelas Importa

No INNER JOIN, trocar as tabelas de lugar não muda o resultado. No LEFT JOIN, muda: a tabela depois de FROM é aquela cujas linhas são todas mantidas. film LEFT JOIN inventory mantém todos os filmes; inventory LEFT JOIN film mantém todas as cópias, e cada cópia tem um filme, então os filmes sem cópias se perdem.


Principais conclusões:

  • O LEFT JOIN mantém cada linha da tabela da esquerda; as linhas sem correspondência recebem NULL nas colunas da direita.
  • LEFT JOIN e LEFT OUTER JOIN são a mesma coisa.
  • LEFT JOIN ... WHERE right_key IS NULL encontra linhas sem correspondência (um anti-join).
  • Um filtro sobre a tabela da direita no WHERE transforma um LEFT JOIN num INNER JOIN; coloque-o no ON.
  • A tabela depois de FROM é aquela cujas linhas são mantidas.

Perguntas Frequentes

Qual é a diferença entre INNER JOIN e LEFT JOIN?

O INNER JOIN devolve apenas as linhas que têm correspondência nas duas tabelas. O LEFT JOIN devolve também as linhas da tabela da esquerda sem correspondência, preenchendo as colunas da direita com NULL.

LEFT JOIN é o mesmo que LEFT OUTER JOIN?

Sim. OUTER é opcional, e as duas formas devolvem o mesmo resultado em qualquer banco de dados.

Por que o meu LEFT JOIN funciona como um INNER JOIN?

Muito provavelmente, a cláusula WHERE tem uma condição sobre uma coluna da tabela da direita. Nas linhas sem correspondência, essa coluna é NULL, a condição não é verdadeira e as linhas são removidas. Mova a condição para a cláusula ON.

Tente resolver as tarefas a seguir para reforcar o que voce aprendeu nesta licao.

  1. Encontre filmes que estavam fora de estoque