SQL Funções Agregadas

Fim do expediente na Loja Aurora. A gerente pergunta para a Ana: "Quantos produtos a gente vende? Qual é o mais caro? Quantas unidades temos no estoque?". Nenhuma dessas perguntas quer uma lista de produtos; cada uma quer um número só. Até agora, todo SELECT que você escreveu devolvia linhas da tabela, uma por uma. Nesta aula você conhece as funções agregadas: elas olham muitas linhas de uma vez e devolvem um resumo. São cinco, e cada uma ganha uma aula própria logo depois desta.

Você vai aprender
  • O que é uma função agregada e por que ela devolve uma linha só.
  • As cinco do dia a dia: COUNT, SUM, AVG, MIN e MAX.
  • A diferença entre uma função comum (que trabalha linha por linha) e uma agregada (que junta as linhas).
  • Como o WHERE escolhe quais linhas entram na conta.
  • O que acontece com os NULL e com uma tabela sem nenhuma linha.
  • Os erros reais do SQLite quando uma agregada vai parar no lugar errado.

Todos os exemplos rodam de verdade no seu navegador, no banco da Loja Aurora (veja O Banco da Loja Aurora). O quadro "Resultado" embaixo de cada código é o que o SQLite devolveu.


A ideia: a linha de total da planilha

Pense na planilha de estoque da loja. Embaixo da coluna de preços, alguém sempre escreve uma linha de total, ou de média. A planilha pega a coluna inteira e "espreme" tudo num valor só:

produto              preço    estoque
Caderno Pautado      12.35      120
Caneta Azul           2.79      500
Lápis HB              1.15      430
...                    ...      ...
─────────────────────────────────────
quantos produtos?       45
estoque total:                 3301

Uma função agregada faz exatamente isso: recebe os valores de muitas linhas e devolve um valor. "Agregar" quer dizer juntar, reunir. As cinco que todo banco de dados tem são estas:

FunçãoEm inglêsPergunta que ela respondeAula
COUNT()count, "contar"Quantas linhas (ou quantos valores) existem?COUNT()
SUM()sum, "soma"Quanto dá tudo somado?SUM()
AVG()average, "média"Qual é a média?AVG()
MIN()minimum, "mínimo"Qual é o menor valor?MIN()
MAX()maximum, "máximo"Qual é o maior valor?MAX()

1. A primeira agregada: contar as categorias

Compare duas consultas na mesma tabela, categorias. A primeira você já conhece: lista os nomes.

SELECT nome FROM categorias;

Oito linhas, uma por categoria. Agora a pergunta "quantas categorias a loja tem?":

SELECT COUNT(*) FROM categorias;
SELECT
"Selecione": o mesmo de sempre. O que vem depois dele diz o que vai aparecer no resultado.
COUNT(…)
A função agregada de contar. Toda função tem um nome e um par de parênteses; dentro deles vai o que ela deve olhar.
*
Aqui, o asterisco quer dizer "a linha inteira": conte as linhas, sem olhar coluna nenhuma. Os detalhes de COUNT(*) e COUNT(coluna) estão na aula COUNT().
FROM categorias
"De categorias": a tabela cujas linhas vão ser contadas.

Repare no resultado: uma linha só, com o número 8. As oito linhas da tabela entraram na conta, mas saiu um resumo. O nome da coluna, COUNT(*), é o próprio texto que você escreveu. Ele fica mais amigável com um apelido (aula Apelidos (AS)):

SELECT COUNT(*) AS categorias FROM categorias;

2. As cinco numa consulta só

Você pode pedir várias agregadas no mesmo SELECT, separadas por vírgula, como faz com colunas. Aqui está o "relatório do fim do dia" da gerente, todo de uma vez:

SELECT COUNT(*)     AS produtos,
       MIN(preco)   AS mais_barato,
       MAX(preco)   AS mais_caro,
       SUM(estoque) AS unidades,
       AVG(preco)   AS preco_medio
FROM produtos;
ParteO que fazResultado
COUNT(*)Conta as linhas da tabela produtos45 produtos
MIN(preco)O menor valor da coluna preco1.15 (o Lápis Grafite HB)
MAX(preco)O maior valor da coluna preco249.9 (o Jogo de Panelas)
SUM(estoque)Soma a coluna estoque de todas as linhas3301 unidades
AVG(preco)A média da coluna preco48.2053333333333

Duas coisas para reparar:

  • As cinco cabem numa linha só. Cada agregada resume a tabela inteira, então todas juntas continuam dando uma linha.
  • A média saiu com muitas casas. O SQLite mostra até 15 algarismos, e não arredonda sozinho para centavos. Para isso existe ROUND(), que você usa na aula AVG(). Repare também que o SQLite escreve 249.9, e não 249.90: ele não completa zeros à direita.

Missão: a categoria Eletrônicos tem o id 2. Mude a consulta para contar só os produtos dessa categoria. Dica: um WHERE (aula WHERE) antes do ponto e vírgula.

SELECT COUNT(*) AS produtos
FROM produtos;
SELECT COUNT(*) AS produtos
FROM produtos
WHERE categoria_id = 2;

3. Função comum × função agregada

Você já viu (ou ainda vai ver) funções que trabalham uma linha de cada vez. A LENGTH(), por exemplo, conta as letras de um texto. Compare:

SELECT nome, LENGTH(nome) AS letras
FROM categorias;

Oito linhas entraram, oito linhas saíram: a LENGTH() rodou uma vez para cada categoria. Uma agregada, não: oito linhas entram e uma sai.

Função comum (escalar)Função agregada
OlhaUma linha por vezTodas as linhas juntas
Linhas no resultadoTantas quantas entraramUma (sem GROUP BY)
ExemplosLENGTH(), UPPER(), ROUND()COUNT(), SUM(), AVG(), MIN(), MAX()
Pergunta típica"Quantas letras tem cada nome?""Quantos nomes existem ao todo?"

"Escalar" é o nome técnico da função comum: ela devolve um valor para cada linha. Guarde a ideia de "muitas entram, uma sai", porque ela explica quase todos os erros desta parte do guia.


4. O WHERE escolhe quem entra na conta

A gerente agora quer o resumo só da Papelaria (a categoria de id 1). Basta pôr um WHERE, como em qualquer consulta:

SELECT COUNT(*)     AS produtos,
       MIN(preco)   AS mais_barato,
       MAX(preco)   AS mais_caro,
       SUM(estoque) AS unidades
FROM produtos
WHERE categoria_id = 1;

O banco trabalha nesta ordem:

  1. FROM produtos: pega as 45 linhas da tabela.
  2. WHERE categoria_id = 1: joga fora as que não são da Papelaria. Sobram 6.
  3. As agregadas: contam, procuram o menor e o maior e somam só essas 6.

Ou seja: o WHERE filtra as linhas antes, e a conta é feita depois, com o que sobrou. Essa ordem vai ser importante na aula HAVING.


5. Os NULL ficam de fora

Lembra do NULL, o "valor desconhecido" (aula O Valor NULL)? Na tabela clientes, três pessoas não têm email cadastrado. Veja o que acontece quando você conta as linhas e quando conta os emails:

SELECT COUNT(*)     AS clientes,
       COUNT(email) AS com_email
FROM clientes;

São 30 clientes, mas só 27 emails: as funções agregadas ignoram os NULL. O COUNT(email) pulou os três clientes sem email. O mesmo vale para SUM, AVG, MIN e MAX: um valor desconhecido não entra na soma, nem na média, nem na disputa de menor e maior. A única que conta tudo é COUNT(*), porque ela conta linhas, e não valores.


6. E se nenhuma linha entrar na conta?

A loja não tem nenhum produto acima de R$ 1000. O que as agregadas devolvem quando o WHERE não deixa passar nada?

SELECT COUNT(*), SUM(estoque), MIN(preco), MAX(preco), AVG(preco)
FROM produtos
WHERE preco > 1000;

Mesmo sem nenhuma linha, o resultado tem uma linha. Uma consulta com agregadas (e sem GROUP BY) sempre devolve exatamente uma linha:

  • COUNT(*) dá 0: contar nada dá zero, faz sentido.
  • SUM, MIN, MAX e AVG dão NULL: não existe "o maior preço" de uma lista vazia. Atenção: a soma de nada também sai NULL, e não 0. A aula SUM() mostra como trocar esse NULL por zero.

7. ORDER BY e LIMIT não mudam a conta

Um engano comum: achar que LIMIT 5 faz o COUNT contar só cinco produtos.

SELECT COUNT(*) FROM produtos LIMIT 5;

Deu 45. O LIMIT (aula LIMIT e OFFSET) corta as linhas do resultado, e o resultado aqui já tem uma linha só; cortar para cinco não muda nada. O mesmo vale para o ORDER BY: ordenar as linhas antes de somar não muda a soma. Para escolher quais linhas entram na conta, quem trabalha é o WHERE.


8. Formas diferentes de saber "quantos"

Para descobrir quantos clientes são VIP, você tem dois caminhos:

SELECT nome FROM clientes WHERE vip = 1;
SELECT COUNT(*) AS vips FROM clientes WHERE vip = 1;

O primeiro mostra os cinco nomes, e o editor escreve o total de linhas embaixo da tabela. O segundo devolve só o número 5. Os dois estão certos; a escolha depende do que você precisa:

SituaçãoUsePor quê
Quer ver quem sãoListar (SELECT nome …)Você precisa dos dados, não só do número
Quer só o númeroCOUNT(*)O banco faz a conta e manda uma linha; numa tabela de 1 milhão de linhas, isso é muito mais rápido do que trazer tudo
Quer o número dentro de um programa (PHP, Python…)COUNT(*)O "N linhas" embaixo da tabela é coisa do editor; o programa recebe só o que o SELECT devolve

No terminal do sqlite3

No programa sqlite3 (aula O Terminal do SQLite), as agregadas funcionam igual. A Ana abriu o banco da loja e pediu o resumo em forma de quadro com .mode box:

$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> SELECT COUNT(*) FROM clientes;
30
sqlite> .mode box
sqlite> SELECT COUNT(*), MIN(preco), MAX(preco), AVG(preco) FROM produtos;
┌──────────┬────────────┬────────────┬──────────────────┐
│ COUNT(*) │ MIN(preco) │ MAX(preco) │    AVG(preco)    │
├──────────┼────────────┼────────────┼──────────────────┤
│ 45       │ 1.15       │ 249.9      │ 48.2053333333333 │
└──────────┴────────────┴────────────┴──────────────────┘
sqlite> .quit

Quando dá errado

Agregada dentro do WHERE

A Ana quer os produtos mais caros que a média e escreve o que parece óbvio:

SELECT nome, preco
FROM produtos
WHERE preco > AVG(preco);

A mensagem: misuse of aggregate function AVG(), "uso indevido da função agregada AVG()". No terminal, ela vem com o prefixo e a seta apontando o lugar:

sqlite> SELECT nome, preco FROM produtos WHERE preco > AVG(preco);
Parse error: misuse of aggregate function AVG()
  SELECT nome, preco FROM produtos WHERE preco > AVG(preco);
                                   error here ---^

Por que acontece: lembre a ordem da seção 4. O WHERE olha uma linha de cada vez, decidindo se ela fica ou sai. Nesse momento a média ainda não existe: ela só pode ser calculada depois, com todas as linhas juntas. Uma agregada nunca pode ficar no WHERE.

Como consertar: calcule a média numa consulta separada e use o número, ou espere a aula Subconsultas, que faz as duas coisas numa consulta só: WHERE preco > (SELECT AVG(preco) FROM produtos).

Um asterisco onde não cabe

O * só funciona no COUNT. Somar "a linha inteira" não faz sentido:

SELECT SUM(*) FROM produtos;

A mensagem: wrong number of arguments to function SUM(), "número errado de argumentos para a função SUM()". Para o SQLite, SUM(*) é um SUM sem nada dentro, e ele precisa de exatamente um valor.

Como consertar: diga qual coluna somar: SUM(estoque).

O nome da função em português
SELECT SOMA(preco) FROM produtos;

A mensagem: no such function: SOMA, "não existe a função SOMA". Os nomes das funções são fixos, em inglês: SUM, AVG, COUNT… Maiúsculas ou minúsculas tanto faz (sum(preco) também funciona); neste guia elas vão em maiúsculas, como as outras palavras do SQL.

Uma coluna comum ao lado da agregada

Este não dá erro, e por isso é perigoso:

SELECT nome, MAX(preco) FROM produtos;

O resultado tem uma linha, mas a tabela tem 45 nomes: qual deles vai na coluna nome? O SQLite tem uma regra especial para esse caso, e a maioria dos outros bancos recusa a consulta com erro. A explicação completa está na aula MAX().

Nos outros bancos

COUNT, SUM, AVG, MIN e MAX fazem parte do SQL padrão e têm o mesmo nome no MySQL, no PostgreSQL e no SQL Server. O que muda entre eles são detalhes, como o tipo do número que a média devolve, e cada aula desta parte mostra a diferença que importa.


Referência rápida

EscritaO que devolveIgnora NULL?Sem nenhuma linha
COUNT(*)Quantas linhasConta todas as linhas0
COUNT(coluna)Quantos valores não NULLSim0
SUM(coluna)A somaSimNULL
AVG(coluna)A médiaSimNULL
MIN(coluna)O menor valorSimNULL
MAX(coluna)O maior valorSimNULL
Mensagem do SQLiteQuer dizer
misuse of aggregate function AVG()Uma agregada num lugar que olha linha por linha, como o WHERE
wrong number of arguments to function SUM()A função recebeu argumentos demais ou de menos (ex.: SUM(*))
no such function: SOMAO nome da função não existe

Exercícios

1. Contar os fornecedores

Complete para saber quantas linhas tem a tabela fornecedores:

SELECT (*) FROM fornecedores;

2. O maior frete

Complete com a função que devolve o maior valor da coluna frete:

SELECT (frete) FROM pedidos;

3. A tabela produtos tem 45 linhas. Quantas linhas tem o resultado de SELECT SUM(estoque), AVG(preco) FROM produtos;?

4. Três clientes têm email NULL. O que acontece com eles em COUNT(email)?

5. Por que WHERE preco > AVG(preco) dá misuse of aggregate function AVG()?

6. Nenhum produto custa mais de R$ 1000. O que devolve SELECT MAX(preco) FROM produtos WHERE preco > 1000;?

Missão final: o resumo da equipe

Missão: a gerente quer, numa linha só, o resumo da tabela funcionarios: quantos funcionários são (funcionarios), o menor salário (menor), o maior salário (maior) e a folha de pagamento, que é a soma de todos os salários (folha). Use esses apelidos, nessa ordem.

SELECT nome, salario
FROM funcionarios;
SELECT COUNT(*)     AS funcionarios,
       MIN(salario) AS menor,
       MAX(salario) AS maior,
       SUM(salario) AS folha
FROM funcionarios;

Resumo

  • Uma função agregada recebe os valores de muitas linhas e devolve um só: COUNT (quantos), SUM (soma), AVG (média), MIN (menor) e MAX (maior).
  • Sem GROUP BY, uma consulta com agregadas devolve sempre uma linha, mesmo que nenhuma linha da tabela entre na conta.
  • O WHERE filtra as linhas antes; as agregadas fazem a conta com o que sobrou. ORDER BY e LIMIT não mudam a conta.
  • As agregadas ignoram os NULL; só COUNT(*) conta todas as linhas.
  • Sem nenhuma linha, COUNT dá 0 e as outras dão NULL.
  • Agregada no WHERE dá misuse of aggregate function: o WHERE olha uma linha por vez.