SQL LEFT JOIN: retorne linhas com correspondências opcionais

SQL LEFT JOIN retorna cada linha da tabela da esquerda e anexa as linhas correspondentes da tabela da direita. Em termos de SQL, ele é um SQL outer join: quando não existe correspondência, as colunas do lado direito são preenchidas com NULL. Use quando você precisa de todos os registros de uma tabela, independentemente de dados relacionados existirem, como listar todos os clientes tenham ou não feito pedidos. LEFT JOIN e LEFT OUTER JOIN são palavras-chave idênticas; OUTER é opcional.

Exemplo de SQL LEFT JOIN para retornar todas as linhas

Saída:

A saída aparecerá aqui...

Saída:

+------+-------+
| nome | total |
+------+-------+
| Alma | 79    |
| Alma | 150   |
| Ben  | NULL  |
| Cara | 45    |
+------+-------+
4 row(s)

Como este exemplo funciona

  1. clientes contém três pessoas; pedidos contém três pedidos vinculados por cliente_id.
  2. LEFT JOIN pedidos o ON o.cliente_id = c.id associa cada cliente a cada pedido que corresponde ao seu id. Alma tem dois pedidos, então gera duas linhas de saída.
  3. Ben não tem pedidos. LEFT JOIN mantém a linha dele e preenche o.total com NULL em vez de removê-lo do resultado (o que o INNER JOIN faria).
  4. ORDER BY c.id, o.id mantém a saída agrupada por cliente e estável dentro de cada cliente. O NULL na coluna total de Ben sinaliza ausência de correspondência, não um pedido com valor zero.

ON vs WHERE: por que a posição do filtro importa

Colocar um filtro da tabela da direita em WHERE em vez de ON é o erro mais comum com LEFT JOIN: isso converte silenciosamente o outer join em inner join.

-- Errado: WHERE remove Ben porque o.total é NULL
SELECT c.nome, o.total
FROM clientes c
LEFT JOIN pedidos o ON o.cliente_id = c.id
WHERE o.total > 50;

-- Certo: ON filtra durante a correspondência da junção; Ben permanece com NULLs
SELECT c.nome, o.total
FROM clientes c
LEFT JOIN pedidos o ON o.cliente_id = c.id AND o.total > 50;

Condições em ON são avaliadas durante a junção: linhas sem correspondência do lado esquerdo ainda aparecem com NULLs. WHERE roda depois da junção, então filtra qualquer linha em que a coluna da direita é NULL, eliminando exatamente as linhas que o LEFT JOIN deveria preservar.

Anti-Join: encontrando linhas sem correspondência

LEFT JOIN combinado com uma verificação IS NULL em uma coluna da tabela da direita é um padrão comum para encontrar registros relacionados ausentes:

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

Isso retorna apenas Ben, o cliente com zero pedidos. O padrão funciona porque o.id é NULL apenas para linhas sem correspondência à esquerda. NOT EXISTS alcança o mesmo resultado e pode ficar mais claro quando nenhuma coluna da tabela da direita é necessária na saída.

LEFT JOIN vs INNER JOIN

LEFT JOININNER JOIN
Todas as linhas da esquerda, NULLs para colunas da direita sem correspondênciaApenas linhas com correspondência nos dois lados
Contagem de linhas ≥ contagem de linhas da tabela da esquerdaContagem de linhas ≤ contagem de linhas da menor tabela
Use quando linhas sem correspondência têm significado (relatórios, verificações de completude)Use quando cada linha do resultado precisa de dados válidos das duas tabelas

Regra: comece com LEFT JOIN quando preservar todas as linhas da esquerda for importante. Troque para INNER JOIN quando linhas sem correspondência forem irrelevantes ou inválidas.

Erros comuns com SQL LEFT JOIN

Multiplicação de linhas em junções um-para-muitos

LEFT JOIN produz uma linha de saída por par correspondente. Se um cliente tem dez pedidos, esse cliente aparece dez vezes. Agregar sem levar isso em conta infla totais.

-- Errado: conta linhas de pedido, não clientes
SELECT COUNT(*) FROM clientes c
LEFT JOIN pedidos o ON o.cliente_id = c.id;

-- Certo: conta clientes distintos
SELECT COUNT(DISTINCT c.id) FROM clientes c
LEFT JOIN pedidos o ON o.cliente_id = c.id;

Usar SELECT * com junções

SELECT * em uma consulta com junção retorna nomes de coluna duplicados (como c.id e o.id), o que causa confusão no código da aplicação. Sempre liste as colunas de que você precisa.

Notas de desempenho

Indexe a chave de junção na tabela da direita (pedidos.cliente_id) para que o mecanismo possa buscar correspondências em vez de varrer. Em tabelas grandes à direita onde apenas um subconjunto de linhas importa, pré-filtre com uma subconsulta ou mova o filtro para a cláusula ON (não WHERE) para reduzir o trabalho da junção mantendo a semântica do LEFT JOIN.

FAQ

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

Nenhuma. LEFT JOIN e LEFT OUTER JOIN são sinônimos definidos pelo padrão SQL. Todos os principais bancos de dados (PostgreSQL, MySQL, SQLite, SQL Server, Oracle) tratam os dois de forma idêntica. A maioria dos desenvolvedores omite OUTER por brevidade.

Por que meu LEFT JOIN retorna menos linhas depois de adicionar uma cláusula WHERE?

Um filtro em WHERE sobre uma coluna da tabela da direita elimina linhas estendidas por NULL, transformando o LEFT JOIN em um INNER JOIN efetivo. Mova a condição para a cláusula ON para manter linhas sem correspondência da esquerda no resultado.

Quando devo usar LEFT JOIN em vez de INNER JOIN?

Use LEFT JOIN quando as linhas do lado esquerdo precisam aparecer na saída independentemente de correspondências: relatórios de completude, verificações de dados ausentes e enriquecimento opcional. Use INNER JOIN quando apenas linhas correspondentes são válidas e linhas sem correspondência devem ser excluídas.