SQL Funções Agregadas

Uma função agregada junta muitas linhas num valor só: quantos clientes, a soma do frete, o preço médio, o produto mais caro. Sozinha, ela resume a tabela inteira; com GROUP BY, resume cada grupo. Esta página reúne todas as funções agregadas do SQLite 3.50, com o resultado real de cada uma na Loja Aurora. As explicações passo a passo estão em O que são Funções Agregadas e nas aulas de cada função.

Como ler as tabelas
  • Exemplo é uma consulta completa; rode-a no editor do guia para conferir.
  • Resultado foi colhido no editor (SQLite 3.50.4), com o banco da Loja Aurora.
  • Toda agregada, menos count(*), ignora os NULL.

As funções

FunçãoO que fazExemploResultadoAula
count(*)Conta as linhas, com ou sem NULL.SELECT count(*) FROM clientes;30COUNT()
count(x)Conta os valores não nulos de x.SELECT count(email) FROM clientes;27 (3 clientes não têm email)COUNT()
count(DISTINCT x)Conta os valores diferentes.SELECT count(DISTINCT cidade) FROM clientes;8COUNT()
sum(x)Soma. Se todos os valores são inteiros, o resultado é inteiro.SELECT sum(estoque) FROM produtos;3301SUM()
Sem nenhuma linha (ou só NULL): NULLSELECT sum(frete) FROM pedidos WHERE id > 1000;NULL
total(x)Soma também, mas sempre devolve número real, e 0.0 quando não há o que somar. Só existe no SQLite.SELECT total(estoque) FROM produtos;3301.0SUM()
SELECT total(frete) FROM pedidos WHERE id > 1000;0.0
avg(x)Média (average). Sempre devolve número real.SELECT avg(preco) FROM produtos;48.2053333333333AVG()
Quase sempre se usa com roundSELECT round(avg(preco), 2) FROM produtos;48.21
min(x)O menor valor.SELECT min(preco) FROM produtos;1.15MIN()
Funciona com texto (ordem alfabética) e com datasSELECT min(data) FROM pedidos;2024-01-05
max(x)O maior valor.SELECT max(preco) FROM produtos;249.9MAX()
SELECT max(data) FROM pedidos;2026-10-14
group_concat(x)Junta os valores num texto só, separados por vírgula.SELECT group_concat(nome) FROM categorias;Papelaria,Eletrônicos,Casa e Cozinha,Livros,Brinquedos,Esporte e Lazer,Alimentos,JardimGROUP BY
group_concat(x, sep)Com o separador que você escolher.SELECT group_concat(nome, ' | ') FROM categorias;Papelaria | Eletrônicos | Casa e Cozinha | Livros | Brinquedos | Esporte e Lazer | Alimentos | Jardim
string_agg(x, sep)Outro nome do group_concat(x, sep) (o separador é obrigatório). É o nome usado no PostgreSQL e no SQL Server.SELECT string_agg(nome, ', ' ORDER BY nome DESC) FROM categorias;Papelaria, Livros, Jardim, Esporte e Lazer, Eletrônicos, Casa e Cozinha, Brinquedos, Alimentos
json_group_array(x)Junta os valores numa lista JSON.SELECT json_group_array(nome) FROM categorias WHERE id <= 3;["Papelaria","Eletrônicos","Casa e Cozinha"]JSON no SQL
json_group_object(chave, valor)Monta um objeto JSON com pares chave → valor.SELECT json_group_object(id, nome) FROM categorias WHERE id <= 3;{"1":"Papelaria","2":"Eletrônicos","3":"Casa e Cozinha"}JSON no SQL

Três jeitos de contar, na mesma consulta. A diferença entre count(*) e count(email) são justamente os 3 clientes sem email:

SELECT count(*) AS todos,
       count(email) AS com_email,
       count(DISTINCT cidade) AS cidades
FROM clientes;

Dentro dos parênteses: DISTINCT, ORDER BY e FILTER

EscritaO que fazExemploResultado
agregada(DISTINCT x)Tira as repetições antes de agregar. Só com um argumento.SELECT group_concat(DISTINCT estado) FROM clientes;PI,SP,CE,PE
Também em avg, sum…SELECT avg(DISTINCT quantidade), avg(quantidade) FROM itens_pedido;2.0 e 1.70454545454545
agregada(x, sep ORDER BY y)Escolhe a ordem em que os valores entram. Importa no group_concat, no string_agg e nas de JSON. Sem ele, a ordem não é garantida.SELECT group_concat(nome, ', ' ORDER BY nome) FROM categorias;Alimentos, Brinquedos, Casa e Cozinha, Eletrônicos, Esporte e Lazer, Jardim, Livros, Papelaria
agregada(x) FILTER (WHERE condição)Só as linhas que passam na condição entram na conta. Permite várias contagens diferentes numa consulta só.SELECT count(*) FILTER (WHERE vip = 1) AS vips, count(*) FILTER (WHERE vip = 0) AS comuns FROM clientes;5 e 25
SELECT count(*) FILTER (WHERE vip = 1) AS vips,
       count(*) FILTER (WHERE vip = 0) AS comuns,
       count(*) AS todos
FROM clientes;

Com GROUP BY

Sem GROUP BY, a agregada resume a tabela inteira numa linha. Com GROUP BY, ela resume cada grupo: uma linha por grupo. Os fornecedores, por estado (os dois estrangeiros têm estado NULL e formam um grupo próprio):

SELECT estado,
       count(*) AS fornecedores,
       group_concat(cidade, ', ') AS cidades
FROM fornecedores
GROUP BY estado;
Onde a agregada pode aparecerPode?Aula
na lista do SELECTsimFunções Agregadas
no HAVING (filtrar grupos)simHAVING
no ORDER BYsimORDER BY
no WHEREnão: misuse of aggregate function SUM()Erros Comuns
no GROUP BYnão: aggregate functions are not allowed in the GROUP BY clauseErros Comuns
com OVER (…)sim: vira uma função de janela (veja Funções de Janela)Funções de Janela

O que cada uma faz com NULL e com "nenhuma linha"

FunçãoIgnora NULL?Sem nenhuma linhaTipo do resultado
count(*)não (conta a linha)0inteiro
count(x)sim0inteiro
sum(x)simNULLinteiro se todos forem inteiros; senão, real
total(x)sim0.0sempre real
avg(x)simNULLsempre real
min(x), max(x)simNULLo do valor
group_concat, string_aggsimNULLtexto
SELECT count(*), sum(frete), total(frete), avg(frete), max(frete)
FROM pedidos
WHERE id > 1000;
Média de texto

Uma agregada numérica numa coluna de texto não dá erro: os textos que não parecem números valem 0.

SELECT avg(nome) FROM clientes;

Se uma conta deu 0.0 sem sentido, confira se a coluna é mesmo numérica.


Mediana e percentis

As funções median(x), percentile(x, p), percentile_cont(x, p) e percentile_disc(x, p) entraram no SQLite principal só na versão 3.51. No 3.50 do guia, elas existem no programa sqlite3 do terminal (ele as traz como extensão embutida), mas não no editor do guia, onde dão no such function: median. No terminal da Ana:

sqlite> SELECT median(preco) FROM produtos;
32.5
sqlite> SELECT percentile(preco, 90), percentile_cont(preco, 0.9), percentile_disc(preco, 0.9) FROM produtos;
95.396|95.396|89.99
median(preco)
A mediana: o preço do meio, quando todos estão em ordem. Metade dos produtos custa menos de 32,50, metade custa mais. Repare como é bem menor que a média (48,21): uns poucos produtos caros puxam a média para cima, mas não a mediana.
percentile(preco, 90)
O percentil 90, com p de 0 a 100: 90% dos preços estão abaixo dele.
percentile_cont(preco, 0.9)
O mesmo, com p de 0 a 1. Pode dar um valor "entre" dois preços (aqui, 95,396 não é preço de nenhum produto).
percentile_disc(preco, 0.9)
Devolve sempre um valor que existe na coluna (89,99).

Nos outros bancos

Quase tudo igual, com três diferenças

COUNT, SUM, AVG, MIN e MAX são do padrão SQL e existem em todos os bancos. As diferenças estão em juntar textos, no FILTER e na média de inteiros. Conferido nas documentações oficiais; trechos só para comparar.

ParaSQLiteMySQLPostgreSQLSQL Server
juntar textosgroup_concat(x, ', ' ORDER BY x) ou string_aggGROUP_CONCAT(x ORDER BY x SEPARATOR ', ')string_agg(x, ', ' ORDER BY x)STRING_AGG(x, ', ') WITHIN GROUP (ORDER BY x)
contar só algumas linhascount(*) FILTER (WHERE …)SUM(CASE WHEN … THEN 1 ELSE 0 END)count(*) FILTER (WHERE …)SUM(CASE WHEN … THEN 1 ELSE 0 END)
média de coluna inteirareal (1.70454545454545)decimalnumeric (decimal)inteiro: AVG de int devolve int e corta as casas. Use AVG(x * 1.0).
soma que nunca é NULLtotal(x)COALESCE(SUM(x), 0) (aula IFNULL e COALESCE)
medianamedian(x) (3.51+, ou no sqlite3)—percentile_cont(0.5) WITHIN GROUP (ORDER BY x)PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x) OVER ()

O jeito com CASE também funciona no SQLite, e serve em qualquer banco (aula CASE).


Experimente

Missão: monte um resumo do catálogo por categoria: para cada categoria_id, quantos produtos ela tem (coluna produtos) e o preço do mais caro (coluna mais_caro). Quer ir além? Acrescente mais_barato e o preço médio com 2 casas.

SELECT categoria_id, preco
FROM produtos;
SELECT categoria_id,
       count(*) AS produtos,
       max(preco) AS mais_caro
FROM produtos
GROUP BY categoria_id;

Resumo

  • count(*) conta linhas; count(x) conta valores não nulos; count(DISTINCT x) conta valores diferentes.
  • sum (NULL quando não há o que somar), total (sempre real, nunca NULL), avg (sempre real), min, max.
  • group_concat(x, sep) = string_agg(x, sep); use ORDER BY dentro dos parênteses para escolher a ordem.
  • FILTER (WHERE …) faz várias contagens diferentes numa consulta só.
  • Agregadas vão no SELECT, no HAVING e no ORDER BY; nunca no WHERE.
  • median e os percentis: no terminal sim, no editor do guia (SQLite 3.50) não.