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.
- 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ção | O que faz | Exemplo | Resultado | Aula |
|---|---|---|---|---|
count(*) | Conta as linhas, com ou sem NULL. | SELECT count(*) FROM clientes; | 30 | COUNT() |
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; | 8 | COUNT() |
sum(x) | Soma. Se todos os valores são inteiros, o resultado é inteiro. | SELECT sum(estoque) FROM produtos; | 3301 | SUM() |
| Sem nenhuma linha (ou só NULL): NULL | SELECT 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.0 | SUM() |
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.2053333333333 | AVG() |
Quase sempre se usa com round | SELECT round(avg(preco), 2) FROM produtos; | 48.21 | ||
min(x) | O menor valor. | SELECT min(preco) FROM produtos; | 1.15 | MIN() |
| Funciona com texto (ordem alfabética) e com datas | SELECT min(data) FROM pedidos; | 2024-01-05 | ||
max(x) | O maior valor. | SELECT max(preco) FROM produtos; | 249.9 | MAX() |
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,Jardim | GROUP 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
| Escrita | O que faz | Exemplo | Resultado |
|---|---|---|---|
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 aparecer | Pode? | Aula |
|---|---|---|
na lista do SELECT | sim | Funções Agregadas |
no HAVING (filtrar grupos) | sim | HAVING |
no ORDER BY | sim | ORDER BY |
no WHERE | não: misuse of aggregate function SUM() | Erros Comuns |
no GROUP BY | não: aggregate functions are not allowed in the GROUP BY clause | Erros 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ção | Ignora NULL? | Sem nenhuma linha | Tipo do resultado |
|---|---|---|---|
count(*) | não (conta a linha) | 0 | inteiro |
count(x) | sim | 0 | inteiro |
sum(x) | sim | NULL | inteiro se todos forem inteiros; senão, real |
total(x) | sim | 0.0 | sempre real |
avg(x) | sim | NULL | sempre real |
min(x), max(x) | sim | NULL | o do valor |
group_concat, string_agg | sim | NULL | texto |
SELECT count(*), sum(frete), total(frete), avg(frete), max(frete) FROM pedidos WHERE id > 1000;
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
pde 0 a 100: 90% dos preços estão abaixo dele. - percentile_cont(preco, 0.9)
- O mesmo, com
pde 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
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.
| Para | SQLite | MySQL | PostgreSQL | SQL Server |
|---|---|---|---|---|
| juntar textos | group_concat(x, ', ' ORDER BY x) ou string_agg | GROUP_CONCAT(x ORDER BY x SEPARATOR ', ') | string_agg(x, ', ' ORDER BY x) | STRING_AGG(x, ', ') WITHIN GROUP (ORDER BY x) |
| contar só algumas linhas | count(*) 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 inteira | real (1.70454545454545) | decimal | numeric (decimal) | inteiro: AVG de int devolve int e corta as casas. Use AVG(x * 1.0). |
| soma que nunca é NULL | total(x) | COALESCE(SUM(x), 0) (aula IFNULL e COALESCE) | ||
| mediana | median(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); useORDER BYdentro dos parênteses para escolher a ordem.FILTER (WHERE …)faz várias contagens diferentes numa consulta só.- Agregadas vão no
SELECT, noHAVINGe noORDER BY; nunca noWHERE. mediane os percentis: no terminal sim, no editor do guia (SQLite 3.50) não.