SQL: COUNT(*) vs COUNT(coluna)

Por Réulison Silva
Réulison Silva
Published on
SQL COUNT(*) vs COUNT(coluna)

No mundo da engenharia de dados e análise de SQL, um dos erros mais sutis — e ao mesmo tempo mais comuns — acontece ao usar LEFT JOIN. Você espera que um cliente sem pedidos retorne 0, mas o relatório teima em mostrar 1.

O problema pode não estar no LEFT JOIN, mas sim no que você está contando.

Vamos analisar o cenário clássico apresentado na imagem:

Representação visual das tabelas relacionais CUSTOMERS e ORDERS, destacando a ausência de registros de pedidos correspondentes para o cliente 103 e o impacto dessa relação nos resultados de contagens utilizando SQL.
Representação visual das tabelas relacionais CUSTOMERS e ORDERS, destacando a ausência de registros de pedidos correspondentes para o cliente 103 e o impacto dessa relação nos resultados de contagens utilizando SQL.

Considere as tabelas CUSTOMERS e ORDERS:

Tabela: CUSTOMERS
customer_id
101
102
103
Tabela: ORDERS
order_idcustomer_id
501101
502101
503102

O cenário:

  • Cliente 101 → 2 pedidos
  • Cliente 102 → 1 pedido
  • Cliente 103 → Nenhum pedido

O requisito: Contar os pedidos de todos os clientes, incluindo aqueles que não fizeram nenhum pedido (zero).

O Problema: COUNT(*) vs COUNT(coluna)

1. COUNT(*) → Conta todas as linhas do JOIN

Quando usamos LEFT JOIN, a linha do cliente 103 é preservada mesmo sem um pedido correspondente. As colunas da tabela de pedidos ficam com valores NULL, mas a linha do cliente ainda existe no resultado.

O COUNT(*) conta essa linha preservada.

SELECT c.customer_id,
       COUNT(*) AS joined_rows
FROM customers c
LEFT JOIN orders o
  ON o.customer_id = c.customer_id
GROUP BY c.customer_id
ORDER BY c.customer_id;

Resultado:

customer_idjoined_rows
1012
1021
1031

O que aconteceu aqui? O COUNT(*) contou a linha do cliente 103, mesmo que os dados do pedido sejam NULL. Resultado: 1 (o que está incorreto para o nosso objetivo).

Detalhamento do comportamento da função de agregação COUNT(*) em um cenário de LEFT JOIN, evidenciando o erro lógico de contagem onde o cliente 103 recebe o valor 1 devido à preservação da linha na junção, apesar de não possuir registros associados.
Detalhamento do comportamento da função de agregação COUNT(*) em um cenário de LEFT JOIN, evidenciando o erro lógico de contagem onde o cliente 103 recebe o valor 1 devido à preservação da linha na junção, apesar de não possuir registros associados.

2. COUNT(o.order_id) → Conta apenas IDs de pedidos não nulos

Neste exemplo, order_id é uma chave primária não nula, com uma linha por pedido. Para o cliente 103, o order_id resultante do JOIN é NULL, então ele não contribui em nada para a contagem.

SELECT c.customer_id,
       COUNT(o.order_id) AS order_count
FROM customers c
LEFT JOIN orders o
  ON o.customer_id = c.customer_id
GROUP BY c.customer_id
ORDER BY c.customer_id;

Resultado:

customer_idorder_count
1012
1021
1030

O que aconteceu aqui? O COUNT(coluna) ignora valores NULL. Como o order_id para o cliente 103 é NULL (não houve correspondência no JOIN), ele não é contado. Resultado: 0 (correto!).

Demonstração do uso da função de agregação COUNT(o.order_id) em um LEFT JOIN, evidenciando o retorno correto do valor zero para o cliente 103, uma vez que a função desconsidera os valores nulos gerados pela ausência de correspondência na tabela de pedidos.
Demonstração do uso da função de agregação COUNT(o.order_id) em um LEFT JOIN, evidenciando o retorno correto do valor zero para o cliente 103, uma vez que a função desconsidera os valores nulos gerados pela ausência de correspondência na tabela de pedidos.

Arquitetura e Detalhes Importantes

Mesmas tabelas. Mesmo JOIN. Significados diferentes. Antes de escolher a função de agregação, você precisa entender dois detalhes cruciais:

1. Substituir COUNT(*) por COUNT(1) não resolve o problema

A constante 1 não é nula, então a linha preservada ainda será contada. O cliente 103 continuaria retornando 1.

2. COUNT(o.order_id) não remove duplicatas

Cuidado com a ilusão de que contar uma coluna específica resolve todos os problemas. Se você adicionar mais JOINs (por exemplo, uma tabela de itens do pedido), um único pedido pode ser multiplicado em várias linhas, inflando a contagem novamente.

Conclusão

Conte a entidade, não apenas as linhas

Antes de escolher um agregador, faça a si mesmo duas perguntas fundamentais:

  1. O que cada linha do JOIN representa?
  2. Qual entidade eu estou tentando contar?
Painel de arquitetura de dados detalhando armadilhas comuns em consultas SQL aggregadas, evidenciando por que a substituição por COUNT(1) falha em tratar valores nulos de um LEFT JOIN e como novos relacionamentos na cláusula JOIN podem inflar incorretamente a métrica COUNT(o.order_id) devido a registros duplicados.
Painel de arquitetura de dados detalhando armadilhas comuns em consultas SQL aggregadas, evidenciando por que a substituição por COUNT(1) falha em tratar valores nulos de um LEFT JOIN e como novos relacionamentos na cláusula JOIN podem inflar incorretamente a métrica COUNT(o.order_id) devido a registros duplicados.

Regras práticas:

Use COUNT(*) quando você precisar do número de linhas resultantes do JOIN.

Use COUNT(identificador_não_nulo) quando você precisar contar entidades correspondentes (como pedidos reais), após verificar se o JOIN preserva uma linha por entidade.

Não conte apenas as linhas que o JOIN cria. Conte a entidade.

Fique ligado

Seja um Expert em Growth

Receba insights práticos sobre marketing, dados, performance e tecnologia direto no seu email.