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.
- O que é uma função agregada e por que ela devolve uma linha só.
- As cinco do dia a dia:
COUNT,SUM,AVG,MINeMAX. - A diferença entre uma função comum (que trabalha linha por linha) e uma agregada (que junta as linhas).
- Como o
WHEREescolhe quais linhas entram na conta. - O que acontece com os
NULLe 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ção | Em inglês | Pergunta que ela responde | Aula |
|---|---|---|---|
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(*)eCOUNT(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;
| Parte | O que faz | Resultado |
|---|---|---|
COUNT(*) | Conta as linhas da tabela produtos | 45 produtos |
MIN(preco) | O menor valor da coluna preco | 1.15 (o Lápis Grafite HB) |
MAX(preco) | O maior valor da coluna preco | 249.9 (o Jogo de Panelas) |
SUM(estoque) | Soma a coluna estoque de todas as linhas | 3301 unidades |
AVG(preco) | A média da coluna preco | 48.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 escreve249.9, e não249.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 | |
|---|---|---|
| Olha | Uma linha por vez | Todas as linhas juntas |
| Linhas no resultado | Tantas quantas entraram | Uma (sem GROUP BY) |
| Exemplos | LENGTH(), 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:
- FROM produtos: pega as 45 linhas da tabela.
- WHERE categoria_id = 1: joga fora as que não são da Papelaria. Sobram 6.
- 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,MAXeAVGdãoNULL: não existe "o maior preço" de uma lista vazia. Atenção: a soma de nada também saiNULL, e não 0. A aula SUM() mostra como trocar esseNULLpor 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ção | Use | Por quê |
|---|---|---|
| Quer ver quem são | Listar (SELECT nome …) | Você precisa dos dados, não só do número |
| Quer só o número | COUNT(*) | 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
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).
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).
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.
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().
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
| Escrita | O que devolve | Ignora NULL? | Sem nenhuma linha |
|---|---|---|---|
COUNT(*) | Quantas linhas | Conta todas as linhas | 0 |
COUNT(coluna) | Quantos valores não NULL | Sim | 0 |
SUM(coluna) | A soma | Sim | NULL |
AVG(coluna) | A média | Sim | NULL |
MIN(coluna) | O menor valor | Sim | NULL |
MAX(coluna) | O maior valor | Sim | NULL |
| Mensagem do SQLite | Quer 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: SOMA | O nome da função não existe |
Exercícios
1. Contar os fornecedores
Complete para saber quantas linhas tem a tabela fornecedores:
2. O maior frete
Complete com a função que devolve o maior valor da coluna frete:
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) eMAX(maior). - Sem
GROUP BY, uma consulta com agregadas devolve sempre uma linha, mesmo que nenhuma linha da tabela entre na conta. - O
WHEREfiltra as linhas antes; as agregadas fazem a conta com o que sobrou.ORDER BYeLIMITnão mudam a conta. - As agregadas ignoram os
NULL; sóCOUNT(*)conta todas as linhas. - Sem nenhuma linha,
COUNTdá 0 e as outras dãoNULL. - Agregada no
WHEREdámisuse of aggregate function: oWHEREolha uma linha por vez.