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.

Você vai aprender
  • Calcular a média de uma coluna, ou de um grupo de linhas com WHERE.
  • Por que o SQLite mostra 48.2053333333333 e como arredondar com ROUND().
  • Que AVG é SUM ÷ COUNT, e a armadilha da divisão de inteiros (3301 / 45 = 73).
  • A diferença entre um zero e um NULL na média.
  • Média simples × média ponderada: duas respostas certas para perguntas diferentes.
  • O que o AVG faz 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 AVG calcula 48.2053333333333; depois o ROUND arredonda 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.

Divisão inteira

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 com NULL dá NULL.
AVG( … )
A média só dos 85 pedidos entregues: 6.0 dias. Os NULL saí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;
FormaO que calculaResultado
AVG(preco_unitario)A média das 220 linhas de item. Um item com 3 unidades conta igual a um item com 150.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 vezes49.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 colunaAVG(coluna)
Mostrar a média em centavosROUND(AVG(coluna), 2)
Dividir duas somas de inteirosSUM(a) * 1.0 / SUM(b) (ou AVG, quando for o caso)
A média pesando pela quantidadeSUM(peso * valor) / SUM(peso)
A média sem os zerosAVG(coluna) … WHERE coluna > 0
A porcentagem de "sim" numa coluna 0/1AVG(coluna) * 100

Quando dá errado

A média de uma data

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.

A média de um texto
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.

A média no WHERE

"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).

AVG com duas colunas
SELECT AVG(preco, estoque) FROM produtos;

A mensagem: wrong number of arguments to function AVG(). Um AVG por coluna: AVG(preco), AVG(estoque).

Nos outros bancos

O que muda é o tipo do resultado, e isso afeta a divisão inteira da seção 2:

  • SQL Server: AVG de uma coluna int devolve int. A média do estoque daria 73, sem as casas decimais! Para ter a média completa, converta antes: AVG(estoque * 1.0).
  • PostgreSQL: AVG de inteiros devolve numeric (decimal exato), com as casas.
  • MySQL: AVG de inteiros ou DECIMAL devolve DECIMAL; de FLOAT/DOUBLE, devolve DOUBLE.
SELECT AVG(estoque * 1.0) AS media FROM produtos;

Referência rápida

EscritaO que fazNa Loja Aurora
AVG(coluna)Média dos valores não NULLAVG(preco) = 48.2053333333333
ROUND(AVG(coluna), 2)A média com 2 casas48.21
SUM(x) / COUNT(x)O mesmo que AVG(x), mas com divisão inteira se x for inteiroSUM(estoque) / COUNT(*) = 73
AVG(DISTINCT coluna)Média dos valores diferentesAVG(DISTINCT frete) = 14.53125
Sem nenhuma linhaNULL
Tipo do resultado no SQLiteSempre REAL, mesmo com inteirosAVG(estoque) = 73.3555555555556
Mensagem do SQLiteQuer 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:

SELECT (estoque) FROM produtos;

2. Em centavos

Complete para mostrar o salário médio com 2 casas decimais:

SELECT (AVG(salario), 2) FROM funcionarios;

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 os NULL nas duas contas.
  • O resultado sai em REAL, muitas vezes com dízima (48.2053333333333). Arredonde com ROUND(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; NULL não entra. Escolha o filtro pela pergunta.
  • Média simples (AVG) e ponderada (SUM(peso * valor) / SUM(peso)) respondem perguntas diferentes.
  • AVG de texto não dá erro: vale o número do começo do texto, ou 0. Não confie nesse resultado.