LEFT JOIN: o erro que o transforma num INNER JOIN
Escreve-se um LEFT JOIN precisamente para incluir as linhas que não têm par do outro lado. E depois acrescenta-se uma condição no WHERE que as deita todas fora, sem uma palavra de aviso.
1. O que se queria
-- Todos os artigos, com o número de comentários aprovados
SELECT a.titulo, count(c.id) AS comentarios
FROM artigos a
LEFT JOIN comentarios c ON c.artigo_id = a.id
GROUP BY a.id;Um artigo sem comentários aparece com zero. É para isso que serve o LEFT.
2. O que se escreve, e estraga
SELECT a.titulo, count(c.id) AS comentarios
FROM artigos a
LEFT JOIN comentarios c ON c.artigo_id = a.id
WHERE c.aprovado = true -- ← aqui
GROUP BY a.id;Os artigos sem comentários trazem NULL nas colunas de c. E NULL = true não é verdadeiro — é desconhecido. O WHERE deita-os fora, e o LEFT JOIN passou a comportar-se como um INNER JOIN.
3. A correção
A condição sobre a tabela da direita pertence ao ON, e não ao WHERE:
SELECT a.titulo, count(c.id) AS comentarios
FROM artigos a
LEFT JOIN comentarios c
ON c.artigo_id = a.id
AND c.aprovado = true -- ← faz parte da junção
GROUP BY a.id;A regra em duas linhas: o ON decide o que emparelha; o WHERE decide que linhas ficam depois de emparelhadas. Numa junção à esquerda, é uma diferença que muda o resultado.
4. Onde o WHERE ainda faz sentido
Para condições sobre a tabela da esquerda, não há problema nenhum:
WHERE a.publicado_em >= '2026-01-01'E há um uso deliberado do WHERE com nulos, que é procurar exatamente o que não tem par:
-- Artigos sem um único comentário
SELECT a.titulo
FROM artigos a
LEFT JOIN comentarios c ON c.artigo_id = a.id
WHERE c.id IS NULL;É o mesmo mecanismo — desta vez usado de propósito.