SQL AVG()
Quanto custa, em média, um produto da Loja Aurora? Quantos dias uma entrega leva? Qual é o salário médio da equipe? A média é a conta da escola: some tudo e divida pela quantidade. Em SQL, ela se chama AVG(), do inglês average, "média". A conta é simples, mas o resultado costuma sair com uma fila de casas decimais, e a média tem pegadinhas com o zero, com o NULL e com a divisão de números inteiros. Esta aula mostra todas, com os números reais da loja.
- Calcular a média de uma coluna, ou de um grupo de linhas com
WHERE. - Por que o SQLite mostra
48.2053333333333e como arredondar comROUND(). - Que
AVGéSUM ÷ COUNT, e a armadilha da divisão de inteiros (3301 / 45 = 73). - A diferença entre um zero e um
NULLna média. - Média simples × média ponderada: duas respostas certas para perguntas diferentes.
- O que o
AVGfaz com textos e datas (sem dar erro).
A ideia: a média do boletim
No boletim, a média de quatro provas com notas 7, 8, 6 e 9 é (7 + 8 + 6 + 9) ÷ 4 = 7,5. São dois passos: somar e dividir pela quantidade. O AVG() faz os dois de uma vez, com todas as linhas da coluna. E, assim como o professor não conta uma prova que o aluno não fez, o AVG() não conta os NULL: eles saem da soma e da quantidade.
1. A primeira média
SELECT AVG(preco) AS preco_medio FROM produtos;
- AVG
- A função de média.
- (preco)
- A coluna cuja média você quer. Como no
SUM, vai uma coluna (ou uma conta), nunca*. - AS preco_medio
- O apelido da coluna do resultado.
O preço médio é 48.2053333333333. Essa fila de 3 é uma dízima: a soma dos preços (2169.24) dividida por 45 não dá um número "redondo". O SQLite mostra até 15 algarismos e para ali; a conta de verdade continuaria para sempre.
Arredondar com ROUND
Ninguém escreve um preço com 13 casas. Embrulhe a média em ROUND(…, 2) para ficar com 2 casas, os centavos:
SELECT ROUND(AVG(preco), 2) AS preco_medio FROM produtos;
- ROUND( … , 2)
- "Arredondar", com 2 casas decimais. Primeiro o
AVGcalcula 48.2053333333333; depois oROUNDarredonda para 48.21. Com 1 casa, daria 48.2; sem o segundo número,ROUND(AVG(preco))dá 48.0.
Com WHERE, a média é só das linhas que passaram. O salário médio de quem trabalha nas vendas:
SELECT AVG(salario) AS media_vendas FROM funcionarios WHERE cargo LIKE 'Vendedor%';
Aqui a média saiu "redonda", 3131.51, porque a conta fechou em duas casas. Repare que o SQLite não escreve zeros sobrando: se fosse 3131.50, ele escreveria 3131.5.
Experimente: a consulta calcula o frete médio dos 90 pedidos, com todas as casas. Arredonde o resultado para centavos (2 casas).
SELECT AVG(frete) AS frete_medio FROM pedidos;
SELECT ROUND(AVG(frete), 2) AS frete_medio FROM pedidos;
2. AVG é SUM dividido por COUNT
Você pode conferir a conta do AVG fazendo os dois passos à mão:
SELECT SUM(preco) AS soma,
COUNT(preco) AS quantos,
SUM(preco) / COUNT(preco) AS na_mao,
AVG(preco) AS com_avg
FROM produtos;
Deu igual. Agora faça a mesma coisa com o estoque, que é uma coluna de inteiros:
SELECT AVG(estoque) AS com_avg,
SUM(estoque) / COUNT(*) AS na_mao,
SUM(estoque) * 1.0 / COUNT(*) AS na_mao_com_1_0
FROM produtos;
O na_mao deu 73, e não 73.36! Quando os dois lados de uma divisão são inteiros (3301 e 45), o SQLite faz divisão inteira: joga fora tudo depois da vírgula, como a divisão "com resto" da escola. Multiplicar por 1.0 transforma a soma num número com decimais, e aí a divisão sai completa. O AVG não tem esse problema: ele sempre devolve um número com decimais (REAL), mesmo quando todos os valores são inteiros.
SELECT 10 / 4 dá 2; SELECT 10 / 4.0 dá 2.5. Basta um dos lados ter decimal para a conta sair completa. Sempre que for dividir duas colunas de inteiros, lembre do * 1.0, ou use AVG.
3. Zero entra na média; NULL não
Onze pedidos tiveram frete grátis: o frete deles é 0, um valor conhecido. Ele entra na média e a puxa para baixo:
SELECT AVG(frete) AS todos_os_pedidos FROM pedidos;
SELECT AVG(frete) AS so_fretes_pagos, COUNT(*) AS pedidos FROM pedidos WHERE frete > 0;
Com os zeros, a média dos 90 pedidos é 14.49; só dos 79 fretes pagos, 16.51. As duas estão certas: respondem perguntas diferentes ("quanto o cliente paga de frete, em média?" e "quanto custa um frete, quando é cobrado?").
Já o NULL fica de fora sozinho. Quantos dias, em média, uma entrega leva? Os 5 pedidos que ainda não chegaram têm entregue_em NULL:
SELECT COUNT(*) AS pedidos,
COUNT(entregue_em) AS entregues,
AVG(julianday(entregue_em) - julianday(data)) AS dias
FROM pedidos;
- julianday(…)
- Transforma uma data num número de dias, para dar para fazer conta.
julianday('2024-01-10') - julianday('2024-01-05')dá 5.0. Ela tem aula própria: Funções de Data. - julianday(entregue_em) - julianday(data)
- Quantos dias cada pedido levou. Nos 5 pedidos pendentes,
entregue_eméNULL, e qualquer conta comNULLdáNULL. - AVG( … )
- A média só dos 85 pedidos entregues: 6.0 dias. Os
NULLsaíram da soma e da contagem, sem você pedir.
Se o AVG contasse os pedidos pendentes como zero dias, a média cairia e a loja pareceria mais rápida do que é. Por isso a regra existe: desconhecido não é zero.
4. Média simples × média ponderada
"Qual é o preço médio dos itens vendidos?" parece uma pergunta só, mas tem duas respostas:
SELECT AVG(preco_unitario) AS media_simples,
SUM(quantidade * preco_unitario) / SUM(quantidade) AS media_por_unidade
FROM itens_pedido;
| Forma | O que calcula | Resultado |
|---|---|---|
AVG(preco_unitario) | A média das 220 linhas de item. Um item com 3 unidades conta igual a um item com 1 | 50.88 |
SUM(quantidade * preco_unitario) / SUM(quantidade) | Quanto custou, em média, cada unidade vendida: o faturamento dividido pelas unidades. Um item com 3 unidades pesa 3 vezes | 49.43 |
A segunda é uma média ponderada (cada valor tem um "peso", aqui a quantidade). Ela saiu mais baixa porque os itens comprados em 2 ou 3 unidades são, em geral, de produtos mais baratos, que agora pesam mais na conta. Não existe uma "certa": escolha pela pergunta.
Mais um truque: média de uma coluna 0/1
Na coluna vip, que só tem 0 e 1, a média é a proporção de VIPs:
SELECT AVG(vip) AS proporcao,
ROUND(AVG(vip) * 100, 1) AS porcentagem
FROM clientes;
5 VIPs em 30 clientes: 0.166666666666667, ou 16.7%.
| Para… | Use |
|---|---|
| A média de uma coluna | AVG(coluna) |
| Mostrar a média em centavos | ROUND(AVG(coluna), 2) |
| Dividir duas somas de inteiros | SUM(a) * 1.0 / SUM(b) (ou AVG, quando for o caso) |
| A média pesando pela quantidade | SUM(peso * valor) / SUM(peso) |
| A média sem os zeros | AVG(coluna) … WHERE coluna > 0 |
| A porcentagem de "sim" numa coluna 0/1 | AVG(coluna) * 100 |
Quando dá errado
A Ana quer a data de nascimento média dos clientes:
SELECT AVG(nascimento) FROM clientes;
O que aconteceu: nenhum erro, e o resultado é 1987.75, que não é data nenhuma. As datas estão guardadas como texto ('1985-03-12'). Para fazer conta, o SQLite lê o começo do texto que parece número, 1985, e para no primeiro -. Ou seja, ele calculou a média dos anos dos 28 clientes com data (os 2 NULL ficaram de fora). Funcionou por acidente; com outro formato de data, daria lixo.
Como consertar: faça a conta com números de verdade: AVG(julianday(nascimento)) dá a média em dias, e as funções de data (aula Funções de Data) transformam o resultado de volta numa data.
SELECT AVG(nome) FROM clientes;
O que aconteceu: 0.0. Um texto que não começa com número vale 0 numa conta, e a média de trinta zeros é zero. Assim como no SUM, o SQLite não avisa.
"Os produtos acima da média":
SELECT nome, preco FROM produtos WHERE preco > AVG(preco);
A mensagem: misuse of aggregate function AVG(), "uso indevido da função agregada AVG()". O WHERE decide linha por linha, e a média só existe depois de olhar todas as linhas.
Como consertar: por enquanto, em dois passos: rode SELECT AVG(preco) FROM produtos;, veja que dá cerca de 48.21, e use WHERE preco > 48.21. Numa consulta só, com uma subconsulta: WHERE preco > (SELECT AVG(preco) FROM produtos) (aula Subconsultas).
SELECT AVG(preco, estoque) FROM produtos;
A mensagem: wrong number of arguments to function AVG(). Um AVG por coluna: AVG(preco), AVG(estoque).
O que muda é o tipo do resultado, e isso afeta a divisão inteira da seção 2:
- SQL Server:
AVGde uma colunaintdevolveint. A média do estoque daria 73, sem as casas decimais! Para ter a média completa, converta antes:AVG(estoque * 1.0). - PostgreSQL:
AVGde inteiros devolvenumeric(decimal exato), com as casas. - MySQL:
AVGde inteiros ouDECIMALdevolveDECIMAL; deFLOAT/DOUBLE, devolveDOUBLE.
SELECT AVG(estoque * 1.0) AS media FROM produtos;
Referência rápida
| Escrita | O que faz | Na Loja Aurora |
|---|---|---|
AVG(coluna) | Média dos valores não NULL | AVG(preco) = 48.2053333333333 |
ROUND(AVG(coluna), 2) | A média com 2 casas | 48.21 |
SUM(x) / COUNT(x) | O mesmo que AVG(x), mas com divisão inteira se x for inteiro | SUM(estoque) / COUNT(*) = 73 |
AVG(DISTINCT coluna) | Média dos valores diferentes | AVG(DISTINCT frete) = 14.53125 |
| Sem nenhuma linha | NULL | |
| Tipo do resultado no SQLite | Sempre REAL, mesmo com inteiros | AVG(estoque) = 73.3555555555556 |
| Mensagem do SQLite | Quer dizer |
|---|---|
misuse of aggregate function AVG() | AVG dentro do WHERE |
wrong number of arguments to function AVG() | Mais de uma coluna (ou *) dentro do AVG |
Exercícios
1. O estoque médio
Complete para calcular a média da coluna estoque:
2. Em centavos
Complete para mostrar o salário médio com 2 casas decimais:
3. Quanto o SQLite devolve em SELECT 7 / 2;?
4. Uma coluna tem os valores 10, 20 e NULL. Quanto dá o AVG dela?
5. E se a coluna tivesse 10, 20 e 0?
6. O que o SQLite faz com AVG(nascimento), se as datas estão guardadas como texto '1985-03-12'?
Missão final: o preço médio dos eletrônicos
Missão: a consulta calcula a média de preço de todos os produtos, com todas as casas. A gerente quer o preço médio só dos Eletrônicos (categoria 2), em centavos.
SELECT AVG(preco) AS preco_medio FROM produtos;
SELECT ROUND(AVG(preco), 2) AS preco_medio FROM produtos WHERE categoria_id = 2;
Resumo
AVG(coluna)soma os valores e divide pela quantidade, ignorando osNULLnas duas contas.- O resultado sai em
REAL, muitas vezes com dízima (48.2053333333333). Arredonde comROUND(AVG(x), 2). - Divisão de dois inteiros no SQLite é inteira:
3301 / 45 = 73. Use* 1.0. - Zero entra na média e a puxa para baixo;
NULLnão entra. Escolha o filtro pela pergunta. - Média simples (
AVG) e ponderada (SUM(peso * valor) / SUM(peso)) respondem perguntas diferentes. AVGde texto não dá erro: vale o número do começo do texto, ou 0. Não confie nesse resultado.