SQL SUM()

Quanto a Loja Aurora já vendeu? Quantas unidades estão paradas no depósito? Quanto a loja gastou com frete? Toda pergunta que começa com "quanto dá tudo junto" é uma soma, e em SQL quem soma é a função SUM() (do inglês sum, "soma"). Nesta aula você soma colunas, soma contas feitas linha a linha (quantidade × preço), aprende a ler os decimais do jeito que o SQLite os escreve e descobre por que a soma de nada não dá zero.

Você vai aprender
  • Somar uma coluna inteira, ou só as linhas de um WHERE.
  • Somar uma conta: SUM(quantidade * preco_unitario), o faturamento.
  • Como o SQLite escreve os decimais (18537.4, e não 18537.40) e quando usar ROUND().
  • Por que a soma de nenhuma linha dá NULL, e como trocar por 0.
  • A diferença entre sum() e total(), uma função só do SQLite.
  • As armadilhas: SUM(DISTINCT …), somar texto e o estouro de inteiro.

A ideia: a fita da calculadora

Na Loja Aurora antiga, a Ana somava as vendas do dia numa calculadora de fita: digitava cada valor, apertava +, e no fim a fita mostrava o total. A SUM() é essa calculadora: ela passa por todas as linhas, pega o valor da coluna (ou da conta) que você pediu e vai somando. No fim, devolve um número só.

Duas regras que valem em toda a aula:

  • A SUM() soma números. Use-a em colunas como estoque, preco, frete, salario.
  • Um NULL é pulado: ele não conta como zero, ele simplesmente não entra na fita.

1. Somar uma coluna

Quantas unidades a loja tem no estoque, somando todos os produtos?

SELECT SUM(estoque) AS unidades
FROM produtos;
SUM
A função de somar.
(estoque)
A coluna cujos valores vão ser somados, um de cada linha. Ao contrário do COUNT, o SUM não aceita *: ele precisa saber o que somar.
AS unidades
O apelido da coluna do resultado.
FROM produtos
As 45 linhas que entram na soma.

Com WHERE, só as linhas escolhidas entram na fita. Quanto a loja gasta por mês com os salários dos vendedores e vendedoras?

SELECT SUM(salario) AS folha_vendas
FROM funcionarios
WHERE cargo LIKE 'Vendedor%';

O LIKE 'Vendedor%' (aula LIKE) pega "Vendedor" e "Vendedora": cinco pessoas. A soma dos cinco salários é 15657.55.


2. Como o SQLite escreve os números

Agora some uma coluna com decimais e compare com a soma do estoque:

SELECT SUM(estoque), typeof(SUM(estoque)),
       SUM(preco),   typeof(SUM(preco))
FROM produtos;

A função typeof() diz o tipo do valor (os tipos têm aula própria: Os Tipos no SQLite). Repare:

  • estoque só tem números inteiros, então a soma é um inteiro, integer: 3301, sem vírgula nenhuma.
  • preco tem decimais (é REAL), então a soma também é real: 2169.24.

E o jeito de escrever os decimais tem três detalhes que pegam quem vem da planilha:

Você talvez espereO SQLite escrevePor quê
2.169,242169.24O separador decimal é o ponto, como em inglês, e não existe separador de milhar
18537,4018537.4Ele não completa zeros à direita: 18537.4 e 18537.40 são o mesmo número
98009800.0Um REAL sem parte decimal ganha .0, para mostrar que não é inteiro

3. Somar uma conta: o faturamento

A tabela itens_pedido guarda, para cada produto de cada pedido, a quantidade e o preco_unitario (o preço do dia da compra). Ela não tem uma coluna "subtotal". Veja os três itens do pedido 1, com a conta feita linha a linha:

SELECT quantidade, preco_unitario,
       quantidade * preco_unitario AS subtotal
FROM itens_pedido
WHERE pedido_id = 1;

Dentro do SUM pode ir uma conta, e não só uma coluna. O SQLite faz a conta em cada linha e soma os resultados:

SELECT SUM(quantidade * preco_unitario) AS produtos
FROM itens_pedido
WHERE pedido_id = 1;
quantidade * preco_unitario
A conta de cada linha: 2 × 12.35 = 24.7; 1 × 2.79 = 2.79; 3 × 1.15 = 3.45.
SUM( … )
Soma os três subtotais: 24.7 + 2.79 + 3.45 = 30.94, o valor dos produtos do pedido 1 (o frete fica à parte, na tabela pedidos).

Sem o WHERE, você tem o faturamento da loja inteira, com todos os 220 itens vendidos:

SELECT SUM(quantidade * preco_unitario) AS faturamento
FROM itens_pedido;

Atenção ao parêntese: é SUM(quantidade * preco_unitario), a soma das multiplicações. SUM(quantidade) * SUM(preco_unitario) seria outra coisa (o total de unidades vezes a soma de todos os preços), um número enorme e sem sentido.

Experimente: a consulta soma as unidades de Eletrônicos (categoria 2). A gerente quer o valor desse estoque em reais: o preço de cada produto vezes as unidades dele, tudo somado. Troque o que vai dentro do SUM.

SELECT SUM(estoque) AS valor_estoque
FROM produtos
WHERE categoria_id = 2;
SELECT SUM(preco * estoque) AS valor_estoque
FROM produtos
WHERE categoria_id = 2;

4. ROUND: o número que você vê e o número guardado

O computador guarda os números com decimais em binário, e muitos decimais "redondos" para nós, como 0.1 ou 1.15, não têm uma forma exata em binário. O SQLite esconde isso ao mostrar o número (ele escreve no máximo 15 algarismos), mas o valor guardado pode ter uma sobrinha minúscula lá no fim. Veja o pedido 1 de novo:

SELECT SUM(quantidade * preco_unitario) AS soma,
       SUM(quantidade * preco_unitario) = 30.94 AS igual,
       SUM(quantidade * preco_unitario) - 30.94 AS diferenca,
       ROUND(SUM(quantidade * preco_unitario), 2) = 30.94 AS igual_com_round
FROM itens_pedido
WHERE pedido_id = 1;

A soma aparece como 30.94, mas a comparação = 30.94 dá 0 (falso)! A coluna diferenca mostra por quê: o valor guardado fica um tiquinho abaixo de 30.94. O e-15 quer dizer "vezes 10 elevado a −15": a diferença é de 0,0000000000000036, invisível na tela, mas suficiente para o = dizer "não é igual". Com ROUND(…, 2) ("arredondar", em inglês), o valor é arredondado para 2 casas e a comparação dá 1 (verdadeiro).

ROUND(valor, 2)
Arredonda o valor para 2 casas decimais. O segundo número é quantas casas manter: ROUND(2169.24, 1) dá 2169.2; sem ele, ROUND(2169.24) arredonda para o inteiro e dá 2169.0.

A regra prática: dinheiro somado, arredonde com ROUND(…, 2) antes de comparar, de guardar ou de mostrar para alguém. O ROUND não acrescenta zeros: ROUND(18537.4, 2) continua 18537.4. Se você precisa do texto com duas casas, para um relatório, use printf('%.2f', …), que devolve o texto 18537.40 (mais em Funções Numéricas):

SELECT ROUND(SUM(quantidade * preco_unitario), 2) AS numero,
       printf('%.2f', SUM(quantidade * preco_unitario)) AS texto
FROM itens_pedido;

5. A soma de nada é NULL

Quanto a loja vendeu de produtos acima de R$ 1000? Nenhum produto custa isso:

SELECT SUM(estoque) AS soma
FROM produtos
WHERE preco > 1000;

O resultado é NULL, e não 0. Para o SQL, a soma de nenhum valor é "desconhecida". Isso atrapalha num relatório ("vendemos NULL reais?") e em contas: NULL + 10 dá NULL. Para trocar por zero, embrulhe a soma em COALESCE ou IFNULL (aula IFNULL e COALESCE): eles devolvem o segundo valor quando o primeiro é NULL.

SELECT COALESCE(SUM(estoque), 0) AS com_coalesce,
       IFNULL(SUM(estoque), 0)   AS com_ifnull
FROM produtos
WHERE preco > 1000;

6. total(): a soma do SQLite que nunca dá NULL

O SQLite tem uma segunda função de soma, total(), que não existe nos outros bancos. Ela soma igualzinho, com duas diferenças:

SELECT SUM(estoque), TOTAL(estoque)
FROM produtos;
SELECT SUM(estoque), TOTAL(estoque)
FROM produtos
WHERE preco > 1000;
SUM()TOTAL()
Sem nenhuma linha (ou só NULL)NULL0.0
Soma de inteirosInteiro: 3301Sempre REAL: 3301.0
Inteiros grandes demaisErro integer overflowNunca dá erro
Existe em outros bancos?Sim, é do padrão SQLNão, só no SQLite

Qual usar? SUM(), quase sempre: ela funciona em qualquer banco, e o NULL da soma vazia se resolve com COALESCE(SUM(x), 0), que também funciona em qualquer banco. A total() é um atalho para quando você sabe que o SQL vai rodar só no SQLite.


7. Formas diferentes de somar o frete

Quanto a loja pagou de frete nos pedidos de 2026? A coluna data está no formato AAAA-MM-DD, então "começa com 2026" serve de filtro:

SELECT SUM(frete) AS frete_2026
FROM pedidos
WHERE data LIKE '2026%';
SELECT SUM(frete) AS frete_2026
FROM pedidos
WHERE data BETWEEN '2026-01-01' AND '2026-12-31';

As duas dão 374.15. E uma terceira forma, que parece igual mas não é:

SELECT SUM(frete)          AS todos,
       SUM(DISTINCT frete) AS distintos
FROM pedidos;

O SUM(DISTINCT frete) soma cada valor de frete uma vez só: o 9.9, que aparece em vários pedidos, entra uma vez. Isso quase nunca é o que você quer num total de dinheiro: dois pedidos com o mesmo frete são dois pagamentos.

FormaQuando usar
SUM(coluna)O total de verdade. É quase sempre esta.
SUM(conta)Quando o valor de cada linha é uma conta, como quantidade * preco_unitario
ROUND(SUM(…), 2)Dinheiro: antes de comparar, guardar ou mostrar
COALESCE(SUM(…), 0)Quando o filtro pode não deixar passar nada e você quer 0, e não NULL
SUM(DISTINCT coluna)Raramente: somar os valores diferentes, não as linhas
TOTAL(coluna)Só no SQLite, quando quer 0.0 em vez de NULL

Quando dá errado

Somar uma coluna de texto

A Ana queria contar os clientes e escreveu SUM no lugar de COUNT:

SELECT SUM(nome) FROM clientes;

O que aconteceu: nenhum erro, e o resultado é 0.0. O SQLite tenta transformar cada texto em número; um texto que não parece número, como "José Ribamar Silva", vale 0. Trinta zeros somados dão 0.0. O SQLite não reclama, então quem tem de perceber é você.

Como consertar: para contar, COUNT. SUM é só para colunas de números.

SUM(*)
SELECT SUM(*) FROM itens_pedido;

A mensagem: wrong number of arguments to function SUM(), "número errado de argumentos para a função SUM()". O * só vale dentro do COUNT.

Como consertar: diga o que somar: SUM(quantidade), ou SUM(quantidade * preco_unitario).

Duas colunas dentro do SUM
SELECT SUM(quantidade, preco_unitario) FROM itens_pedido;

A mensagem: a mesma, wrong number of arguments to function SUM(). A vírgula separa argumentos, e o SUM aceita um só.

Como consertar: se você quer a soma das multiplicações, escreva a conta: SUM(quantidade * preco_unitario). Se quer duas somas, use dois SUM: SUM(quantidade), SUM(preco_unitario).

Inteiros grandes demais: integer overflow

O maior inteiro que o SQLite guarda é 9223372036854775807 (mais de 9 quintilhões). Somar além disso com SUM dá erro. Para ver, a consulta abaixo usa uma tabelinha de duas linhas criada na hora com VALUES (a coluna dela se chama column1):

SELECT SUM(column1)
FROM (VALUES (9223372036854775807), (1));

A mensagem: integer overflow, "estouro de inteiro": o resultado não cabe. A TOTAL(column1) não dá erro, porque soma em REAL; ela devolve 9.22337203685478e+18 (9,22 × 1018), um número aproximado. Na Loja Aurora isso nunca acontece, mas é bom saber que a mensagem existe.

Nos outros bancos
  • total() é só do SQLite. Nos outros, use COALESCE(SUM(x), 0), que funciona em todos. A documentação do PostgreSQL avisa o mesmo que você viu aqui: a soma de nenhuma linha é NULL, e não zero.
  • PostgreSQL: SUM de uma coluna integer devolve bigint, um inteiro maior, e de numeric devolve numeric, um decimal exato, sem a sobrinha binária da seção 4.
  • MySQL: SUM de inteiros ou de DECIMAL devolve DECIMAL (exato); de FLOAT ou DOUBLE, devolve DOUBLE.
  • Nesses bancos, dinheiro costuma ficar em colunas DECIMAL/NUMERIC, que guardam os centavos exatos. O SQLite não tem esse tipo; por isso o ROUND(…, 2) é tão importante aqui (veja Os Tipos no SQLite).

Referência rápida

EscritaO que fazNa Loja Aurora
SUM(coluna)Soma os valores, pulando NULLSUM(estoque) = 3301
SUM(a * b)Faz a conta em cada linha e somafaturamento = 18537.4
ROUND(SUM(x), 2)Arredonda a soma para 2 casas
COALESCE(SUM(x), 0)0 em vez de NULL quando não há linhas
SUM(DISTINCT x)Soma cada valor diferente uma vezSUM(DISTINCT frete) = 116.25
TOTAL(x)Só SQLite: soma em REAL, 0.0 sem linhas, nunca estouraTOTAL(estoque) = 3301.0
Mensagem do SQLiteQuer dizer
wrong number of arguments to function SUM()SUM(*) ou mais de um valor dentro do SUM
integer overflowA soma de inteiros passou de 9223372036854775807
misuse of aggregate function SUM()SUM dentro do WHERE (veja HAVING)

Exercícios

1. Unidades vendidas

Complete para somar todas as unidades vendidas (a coluna quantidade de itens_pedido):

SELECT (quantidade) FROM itens_pedido;

2. Duas casas

Complete para arredondar a soma dos fretes para 2 casas decimais:

SELECT (SUM(frete), ) FROM pedidos;

3. Nenhum pedido tem frete acima de 100. O que devolve SELECT SUM(frete) FROM pedidos WHERE frete > 100;?

4. Como o SQLite escreve a soma de 9000.25 com 9000.15?

5. Qual consulta dá o faturamento (o valor de todos os itens vendidos)?

6. Qual a diferença entre SUM(estoque) e TOTAL(estoque) na tabela inteira de produtos?

Missão final: o faturamento de 2025

Missão: os pedidos de 2025 são os de id 33 a 64. A consulta soma as unidades vendidas nesses pedidos. A gerente quer o faturamento deles em reais: a soma de quantidade × preço unitário de todos os itens desses pedidos. Se quiser, arredonde para 2 casas.

SELECT SUM(quantidade) AS faturamento_2025
FROM itens_pedido
WHERE pedido_id BETWEEN 33 AND 64;
SELECT ROUND(SUM(quantidade * preco_unitario), 2) AS faturamento_2025
FROM itens_pedido
WHERE pedido_id BETWEEN 33 AND 64;

Resumo

  • SUM(coluna) soma os valores de todas as linhas (ou só as do WHERE), pulando os NULL.
  • Dentro do SUM pode ir uma conta: SUM(quantidade * preco_unitario) faz a conta linha a linha e soma.
  • O SQLite escreve 18537.4: ponto decimal, sem separador de milhar, sem zeros à direita. Soma de inteiros dá inteiro; de REAL, dá REAL.
  • O decimal guardado pode ter uma sobrinha binária: arredonde dinheiro com ROUND(…, 2) antes de comparar.
  • A soma de nenhuma linha é NULL. Troque por zero com COALESCE(SUM(x), 0).
  • TOTAL() é a soma do SQLite que dá 0.0 em vez de NULL e sempre devolve REAL; não existe nos outros bancos.