Você escreve um LEFT JOIN porque quer manter toda a tabela principal. Até aí, tudo certo.
Depois adiciona um filtro simples no WHERE, roda a query, e sem perceber transforma o resultado em algo muito mais próximo de um INNER JOIN.
Esse erro aparece o tempo todo em análise real. Principalmente em perguntas como:
- clientes sem compra;
- produtos sem venda;
- leads que não avançaram de etapa;
- pedidos sem pagamento confirmado.
Esta Dica Rápida existe para evitar exatamente esse tropeço.
O que está acontecendo, na prática
O LEFT JOIN preserva todas as linhas da tabela da esquerda. Quando não há correspondência na tabela da direita, as colunas da direita vêm como NULL.
O problema surge quando você escreve algo assim:
SELECT c.id_cliente, p.status
FROM clientes c
LEFT JOIN pedidos p
ON c.id_cliente = p.id_cliente
WHERE p.status = 'pago';
Na intenção, parece que você quer “clientes e seus pedidos pagos”. Na prática, o WHERE p.status = 'pago' remove todas as linhas em que p.status veio NULL. Ou seja: remove exatamente os clientes sem correspondência.
Resultado: você perdeu o efeito principal do LEFT JOIN.
Como usar este checklist
Antes de concluir que a query está certa, valide quatro coisas em cada etapa:
- o que a pergunta de negócio realmente quer manter;
- onde o filtro deveria morar;
- como os
NULLs estão sendo tratados; - qual leitura muda quando você compara antes e depois do filtro.
Checklist: seu LEFT JOIN ainda está sendo LEFT?
1) A pergunta exige preservar a tabela da esquerda?
O que validar: confirmar se o objetivo da análise é manter todos os registros da tabela principal, inclusive os sem correspondência.
Sinal verde: você consegue dizer claramente algo como “quero todos os clientes, inclusive os que não compraram”.
Erro comum: escrever LEFT JOIN por hábito, sem perceber que a pergunta real aceitava apenas registros com match.
Ação rápida: reescreva a pergunta em linguagem simples antes de mexer na query. Se o “inclusive os sem…” faz parte da frase, o LEFT JOIN precisa continuar vivo até o resultado final.
2) O filtro pertence ao relacionamento ou ao resultado final?
O que validar: entender se o critério define quais linhas podem participar da junção ou se ele deve ser aplicado depois que o resultado completo já existe.
Sinal verde: você sabe explicar por que o filtro está no ON ou no WHERE, e não apenas “porque funcionou”.
Erro comum: colocar no WHERE uma condição que na verdade fazia parte da lógica da correspondência entre as tabelas.
Ação rápida: teste mover a condição para o ON:
SELECT c.id_cliente, p.status
FROM clientes c
LEFT JOIN pedidos p
ON c.id_cliente = p.id_cliente
AND p.status = 'pago';
Nesse desenho, todos os clientes continuam aparecendo. Só os pedidos considerados válidos para a junção passam pelo critério.
3) Você está tratando NULL de propósito?
O que validar: revisar o que acontece com as linhas sem match depois do JOIN.
Sinal verde: existe decisão explícita sobre manter, destacar ou excluir registros com NULL.
Erro comum: esquecer que WHERE p.status = 'pago' não retorna TRUE para NULL; retorna uma condição que elimina a linha.
Ação rápida: quando fizer sentido para a pergunta, trate os casos sem correspondência de forma clara, por exemplo com p.id_pedido IS NULL ou com um CASE de classificação.
4) A contagem muda de forma coerente depois do filtro?
O que validar: comparar o volume de linhas antes do filtro, depois do JOIN e depois do WHERE.
Sinal verde: a queda de volume é esperada e explicável.
Erro comum: olhar apenas o resultado final e não perceber em que etapa a base perdeu os registros que deveriam continuar.
Ação rápida: monte uma versão de auditoria com COUNT(*) por etapa ou com CTEs simples para inspecionar onde a redução acontece.
5) O seu caso é “sem correspondência” ou “correspondência com critério”?
O que validar: distinguir dois cenários que parecem parecidos, mas pedem SQL diferente.
Sinal verde: você sabe se quer encontrar quem não tem match nenhum ou quem tem match, mas não atende a uma regra específica.
Erro comum: tentar responder os dois problemas com a mesma query e acabar confundindo ausência de registro com registro fora do critério.
Ação rápida: se a análise é sobre “quem não tem pedido”, a lógica é uma. Se é sobre “quem não tem pedido pago”, a lógica é outra. Escreva isso no comentário da query ou na documentação do painel.
6) O WHERE está filtrando a tabela errada?
O que validar: observar se filtros da tabela da direita estão entrando no WHERE quando deveriam estar isolados no ON.
Sinal verde: filtros da esquerda permanecem no WHERE sem quebrar a preservação da base; filtros da direita são posicionados com intenção.
Erro comum: misturar no mesmo WHERE condição da tabela principal com condição da tabela secundária e perder legibilidade do que cada uma faz.
Ação rápida: separe mentalmente as regras em dois grupos: filtros da base principal e filtros da relação. Isso costuma deixar o SQL correto e mais fácil de revisar.
7) Você conseguiria explicar isso para outra pessoa em 30 segundos?
O que validar: testar se a lógica da query está simples o bastante para ser explicada sem ambiguidade.
Sinal verde: sua explicação soa assim: “Quero todos os clientes e, quando houver pedido pago, trazer essa informação.”
Erro comum: depender de um resultado “que parece certo” sem conseguir justificar por que certas linhas sumiram.
Ação rápida: antes de publicar a análise, explique em voz alta o comportamento esperado para três casos: com match, sem match e match fora do critério.
Regra prática para não cair nesse erro
Se a condição faz parte da definição do relacionamento entre as tabelas, desconfie de colocá-la no WHERE.
Se a condição serve para filtrar o resultado final depois que você já aceitou perder linhas sem match, aí o WHERE pode ser o lugar correto.
O ponto central é este: não basta saber a sintaxe do LEFT JOIN; você precisa saber em que momento o filtro muda a semântica da consulta.
Um teste rápido que quase sempre salva
Quando houver dúvida, compare lado a lado:
- a query com o filtro no
WHERE; - a query com o filtro no
ON; - a contagem de linhas e de chaves principais nos dois casos.
Essa comparação normalmente mostra em segundos se você preservou a intenção original ou se matou os NULLs sem perceber.
Se quiser aprofundar este tema, estes conteúdos ajudam como próxima leitura:
