Menu
Testar no Playground

Funções de Agregação no SQLite: COUNT, SUM, AVG, MIN, MAX

Entenda como as funções de agregação do SQLite reduzem várias linhas a um único valor — COUNT, SUM, AVG, MIN, MAX, GROUP_CONCAT, além de DISTINCT, FILTER e o comportamento com NULL.

Esta página tem editores executáveis — edite, execute e veja a saída na hora.

O que uma função de agregação realmente faz

A maioria das funções SQL que você viu até agora trabalha linha a linha: UPPER(name) roda uma vez por linha, ROUND(price, 2) roda uma vez por linha. As funções de agregação no SQLite funcionam de outro jeito. Elas olham para um conjunto inteiro de linhas e condensam tudo em um único valor.

Crie uma tabelinha para brincar:

Cinco linhas entram, uma linha sai. Esse é o modelo mental por trás das funções de agregação no SQLite: elas comprimem várias linhas em um resumo. Sem um GROUP BY, esse resumo cobre todas as linhas do resultado.

SQL COUNT: linhas versus valores

O COUNT tem três variações, e a diferença entre elas faz toda a diferença:

  • COUNT(*) conta linhas. NULLs entram na contagem. Sempre retorna um número.
  • COUNT(coluna) conta os valores não-NULL daquela coluna.
  • COUNT(DISTINCT coluna) conta os valores únicos não-NULL.

São cinco linhas, três delas com amount preenchido e três clientes distintos. Se um dia você se deparar com COUNT(amount) e estranhar por que ele é menor que COUNT(*), é justamente por isso — NULLs ficam de fora.

SUM, AVG, MIN e MAX

As funções de agregação aritméticas se comportam como você imagina, com uma regrinha discreta: todas ignoram NULL:

AVG é (10 + 20 + 30) / 3 = 20.0, e não 60 / 4 = 15.0. O denominador conta apenas os valores não nulos. Se esse não for o comportamento que você quer — ou seja, se preferir tratar dados ausentes como zero — deixe isso explícito:

MIN e MAX também funcionam com texto e datas: no caso do texto, a comparação é lexicográfica; nas datas, basta usar o formato ISO padrão, que elas são comparadas como strings.

Diferença entre SUM e TOTAL no SQLite

O SQLite tem um segundo agregador parecido com soma, o TOTAL, que resolve dois incômodos do SUM:

  • SUM em zero linhas retorna NULL. Já TOTAL retorna 0.0.
  • SUM quando todos os valores são NULL também retorna NULL. TOTAL retorna 0.0.
  • TOTAL sempre devolve um número de ponto flutuante, então nunca dá overflow de inteiro.

O preço dessa praticidade: TOTAL não faz parte do SQL padrão, e o resultado sempre REAL pode te pegar de surpresa se você esperava um inteiro. Use TOTAL quando "nenhuma linha significa zero" fizer sentido na sua aplicação, e fique com SUM quando quiser o comportamento padrão do SQL.

DISTINCT dentro de funções de agregação

O DISTINCT pode ser usado dentro de qualquer função de agregação, não só no COUNT. Ele remove os valores duplicados antes da agregação rodar:

SUM(amount) soma o valor de todas as linhas. Já SUM(DISTINCT amount) soma cada valor único uma única vez — útil em casos como "total de valores únicos de notas fiscais", mas raramente é o que você quer. O mais usado no dia a dia é o COUNT(DISTINCT customer).

FILTER: agregando apenas um subconjunto

Quando você quer agregar só algumas linhas, o caminho óbvio é o WHERE. O problema é que o WHERE filtra tudo — não dá pra "contar pedidos pagos" e "contar reembolsos" na mesma consulta desse jeito. É aí que entra o FILTER:

Cada cláusula FILTER (WHERE ...) se aplica apenas àquele agregado específico. Você faz uma única varredura na tabela e obtém vários recortes resumidos de uma vez. Antes do FILTER existir, a galera escrevia SUM(CASE WHEN status = 'paid' THEN amount END) — mesma ideia, só que com bem mais código.

GROUP_CONCAT no SQLite: juntando strings

O GROUP_CONCAT é o patinho feio do grupo. Em vez de devolver um número, ele concatena os valores em uma única string:

O separador padrão é a vírgula. Se quiser outro caractere, basta passar um segundo argumento. A ordem não é garantida, a menos que você use GROUP_CONCAT(tag ORDER BY tag) — isso é útil quando o resultado vai aparecer em alguma tela e você quer manter a saída consistente.

Agregação sem GROUP BY

Todos os exemplos anteriores que usaram funções de agregação sem GROUP BY retornaram exatamente uma linha. E essa é a regra: um SELECT com agregações e sem GROUP BY gera um resumo de linha única da tabela inteira (depois de aplicado o WHERE).

Você pode combinar várias agregações à vontade:

O que você não pode fazer é misturar colunas não agregadas com funções de agregação e esperar resultados que façam sentido:

-- Permitido pelo SQLite, mas o valor de `customer` é arbitrário.
SELECT customer, SUM(amount) FROM orders;

O SQLite não vai reclamar disso (outros bancos vão), mas ele vai escolher o nome de algum cliente aleatório para mostrar junto com o total. Se você quer a soma por cliente, precisa do GROUP BY — que é o assunto da próxima página.

A seguir: GROUP BY e HAVING

Agregações sobre a tabela inteira respondem "quanto deu no total". Já as agregações por grupo — por cliente, por mês, por status — respondem perguntas bem mais interessantes. O GROUP BY é a forma de separar as linhas em baldes antes de agregar, e o HAVING é como você filtra em cima do resultado agregado. É isso que vem na sequência.

Perguntas frequentes

O que são funções de agregação no SQLite?

Funções de agregação pegam várias linhas e devolvem um único valor de resumo. As nativas são COUNT, SUM, AVG, MIN, MAX, TOTAL e GROUP_CONCAT. Sem um GROUP BY, elas reduzem todo o resultado da consulta a uma só linha.

Qual a diferença entre SUM e TOTAL no SQLite?

Os dois somam números, mas o SUM retorna NULL quando todas as entradas são NULL e usa aritmética inteira sempre que possível (o que pode causar overflow). Já o TOTAL sempre devolve um número de ponto flutuante e retorna 0.0 quando não há linhas. Use TOTAL quando precisar garantir um resultado numérico, e SUM quando o comportamento padrão do SQL for importante.

Como contar valores distintos no SQLite?

Coloque o DISTINCT dentro da chamada: COUNT(DISTINCT customer_id). Isso conta os valores únicos não nulos. O COUNT(coluna) simples conta valores não nulos incluindo duplicatas, e o COUNT(*) conta todas as linhas, independentemente de NULLs.

As funções de agregação do SQLite ignoram NULL?

Sim — todas as agregações, com exceção do COUNT(*), descartam entradas NULL. O AVG, por exemplo, divide pela quantidade de valores não nulos, e não pelo total de linhas. A exceção é o COUNT(*), que conta linhas e não valores, então os NULLs entram na conta.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR