SQL Funções Numéricas

A Loja Aurora vive de números: preços, estoques, fretes, salários. A Ana precisa de contas o tempo todo: o preço com 10% de desconto, arredondado em centavos; quantas caixas de 12 unidades cabem no estoque; a porcentagem de clientes VIP. O SQL faz essas contas direto no SELECT. Nesta aula você vai ver as operações e as funções numéricas do SQLite, e duas armadilhas que pegam até gente experiente: a divisão inteira (no SQLite, 7 / 2 dá 3!) e os números "quebrados" do ponto flutuante.

Você vai aprender
  • As quatro operações e o resto da divisão (%).
  • Por que 7 / 2 dá 3 e 7 / 2.0 dá 3.5, e como isso estraga médias e porcentagens.
  • round para arredondar em casas decimais, e por que ele não mostra 4.90.
  • ceil, floor e trunc: arredondar para cima, para baixo e cortar.
  • abs, sign e as funções matemáticas: sqrt, pow, log, pi…
  • random(), o sorteio, e por que ele não serve para resultados fixos.
  • Por que 0.1 + 0.2 = 0.3 dá "falso".

Os exemplos rodam de verdade no seu navegador, num SQLite com o banco da Loja Aurora, e cada exemplo parte do banco limpo. As funções matemáticas (sqrt, pow, log…) são opcionais no SQLite; o editor do guia e o sqlite3 da Ana vêm com elas ligadas, assim como as versões oficiais do SQLite para Linux, Windows e Mac.


A ideia: a calculadora do caixa

No caixa da loja, a calculadora faz as contas e a Ana decide o que fazer com o resultado: arredondar os centavos, arredondar para cima ("preciso de 3 caixas, não de 2,9"), cortar a parte quebrada. O SQL tem a calculadora (os operadores + - * / %) e as decisões (as funções round, ceil, floor…). Como nas Funções de Texto, nada disso muda a tabela: só o que o SELECT mostra.


1. As operações

SELECT nome, preco, estoque,
       preco * estoque     AS valor_em_estoque,
       preco + 5           AS mais_5,
       preco - 1.5         AS menos_1_e_50
FROM produtos
WHERE id IN (1, 2, 3);
OperadorFazExemploResultado
+Soma7 + 29
-Subtração7 - 25
*Multiplicação7 * 214
/Divisão7 / 23 (!) — seção 2
%Resto da divisão7 % 21

A ordem é a da escola: *, / e % antes de + e -. Na dúvida, use parênteses: (preco + 5) * 2. E NULL em qualquer conta dá NULL (aula O Valor NULL).


2. A divisão inteira: 7 / 2 = 3

Esta é a armadilha número um do SQLite. Veja:

SELECT 7 / 2    AS inteiro_por_inteiro,
       7 / 2.0  AS com_ponto_zero,
       7.0 / 2  AS outro_com_ponto,
       7 % 2    AS resto,
       -7 / 2   AS negativo;

Quando os dois lados da divisão são números inteiros (INTEGER), o SQLite faz uma divisão inteira: divide e joga fora a parte depois da vírgula. 7 / 2 é 3,5, e o SQLite fica só com o 3. É como dividir 7 bombons entre 2 crianças sem partir nenhum: cada uma ganha 3, e o resto (7 % 2) é 1 bombom. Com números negativos, o corte é em direção ao zero: -7 / 2 dá -3.

Basta um dos lados ser número real (com ponto) para a divisão ser "de verdade": 7 / 2.0 e 7.0 / 2 dão 3.5.

Onde isso morde: médias e porcentagens

A coluna estoque é inteira. A Ana quis calcular a média de estoque dos produtos "na mão", somando e dividindo pela quantidade:

SELECT SUM(estoque)                AS total,
       COUNT(*)                    AS produtos,
       SUM(estoque) / COUNT(*)     AS media_na_mao,
       AVG(estoque)                AS media_avg
FROM produtos;

3301 ÷ 45 é 73,355…, mas a conta "na mão" deu 73: inteiro dividido por inteiro. A AVG (aula AVG()) sempre devolve um número real, por isso acertou. Com porcentagens o estrago é maior:

SELECT SUM(vip)                              AS vips,
       COUNT(*)                              AS clientes,
       SUM(vip) * 100 / COUNT(*)             AS pct_errada,
       ROUND(SUM(vip) * 100.0 / COUNT(*), 1) AS pct_certa
FROM clientes;

5 VIPs em 30 clientes são 16,67%. A conta só com inteiros deu 16; trocando o 100 por 100.0, a conta inteira passa a ser real e dá 16.7 (com o ROUND da seção 3).

Jeito de evitar a divisão inteiraExemplo
Escrever um dos números com .0SUM(vip) * 100.0 / COUNT(*)
Multiplicar por 1.0SUM(estoque) * 1.0 / COUNT(*)
Converter com CASTCAST(SUM(estoque) AS REAL) / COUNT(*)
Usar a função pronta, quando existeAVG(estoque)

Experimente: a média de estoque "na mão" saiu 73, sem as casas decimais. Mude a conta para ela dar o valor certo, 73,3…, sem usar AVG.

SELECT SUM(estoque) / COUNT(*) AS media
FROM produtos;
SELECT SUM(estoque) * 1.0 / COUNT(*) AS media
FROM produtos;

A divisão inteira também é útil

Às vezes cortar a parte quebrada é exatamente o que se quer. Os produtos vão para caixas de 12 unidades: quantas caixas cheias dá para montar, e quantas unidades sobram?

SELECT nome, estoque,
       estoque / 12 AS caixas_cheias,
       estoque % 12 AS sobram
FROM produtos
WHERE id IN (1, 5, 11, 14);

A mochila tem 35 no estoque: 2 caixas cheias (24 mochilas) e sobram 11. A divisão inteira dá as caixas; o % dá a sobra. Um uso clássico do % é descobrir se um número é par: id % 2 = 0.


3. round: arredondar

round ("arredondar") recebe o número e quantas casas decimais manter. Sem o segundo argumento, arredonda para o inteiro mais próximo:

SELECT nome, preco,
       preco * 0.9            AS com_desconto,
       round(preco * 0.9, 2)  AS em_centavos,
       round(preco * 0.9)     AS em_reais
FROM produtos
WHERE id IN (1, 7, 14);
preco * 0.9
10% de desconto (paga-se 90% do preço). O caderno de 12.35 sai por 11.115: três casas, que não existem em dinheiro.
round(…, 2)
Duas casas, os centavos: 11.115 vira 11.12. A metade (o 5) arredonda para cima.
round(…)
Sem casas: o real mais próximo, 11.0.

Repare que o round sempre devolve um número real: mesmo round(…) sem casas mostra 11.0, com o .0.

O round não "completa" as casas

Um engano comum é achar que o round(x, 2) mostra sempre duas casas:

SELECT nome, salario,
       round(salario * 1.05, 2)              AS com_aumento,
       printf('%.2f', salario * 1.05)        AS com_aumento_2_casas
FROM funcionarios
WHERE id IN (2, 8);

O Davi passa a ganhar 2782.5, e não 2782.50. O round arredonda o número; o número 2782.50 é o mesmo que 2782.5, e o SQLite escreve o mais curto. Para mostrar duas casas, quem resolve é a printf (aula Funções de Texto), que devolve texto. A regra: round para fazer contas e guardar; printf para exibir.

As metades

SELECT round(2.5)       AS dois_e_meio,
       round(-2.5)      AS menos_dois_e_meio,
       round(3.14159, 2) AS pi_2_casas,
       round(2.675, 2)  AS surpresa;

No SQLite, a metade arredonda para longe do zero: 2.5 vira 3.0 e -2.5 vira -3.0. E o 2.675? Deveria dar 2.68, mas deu 2.67. Não é defeito do round: o computador não consegue guardar 2,675 exato e guarda algo como 2,67499999…, que arredonda para baixo. A seção 6 explica.


4. ceil, floor e trunc: para cima, para baixo, cortar

O round vai para o mais perto. Às vezes você precisa de uma direção:

SELECT ceil(4.1)    AS ceil_41,
       floor(4.9)   AS floor_49,
       trunc(4.9)   AS trunc_49,
       ceil(-4.9)   AS ceil_menos_49,
       floor(-4.1)  AS floor_menos_41,
       trunc(-4.9)  AS trunc_menos_49;
FunçãoFaz4.9-4.9
ceil(x) ou ceiling(x)Para cima: o menor inteiro que não é menor que x (ceiling = "teto")5.0-4.0
floor(x)Para baixo: o maior inteiro que não é maior que x (floor = "chão")4.0-5.0
trunc(x)Corta a parte depois do ponto (truncate), em direção ao zero4.0-4.0
round(x)O inteiro mais perto5.0-5.0

Para números positivos, floor e trunc dão o mesmo; a diferença aparece nos negativos. O caso de uso clássico do ceil é "quantas caixas eu preciso para guardar tudo?": com 35 mochilas e caixas de 12, são 2,9 caixas, e ninguém compra 0,9 caixa: são 3.

SELECT nome, estoque,
       estoque / 12        AS caixas_cheias,
       ceil(estoque / 12.0) AS caixas_precisas
FROM produtos
WHERE id IN (1, 5, 11, 14);

Repare no 12.0: com estoque / 12, a divisão inteira já cortaria o 2,9 para 2 antes do ceil agir, e o ceil(2) continuaria 2.


5. abs, sign e as funções matemáticas

abs ("absoluto") tira o sinal: a distância até o zero. sign ("sinal") diz só o sinal: -1, 0 ou 1. Servem, por exemplo, para comparar o preço de catálogo com o preço cobrado num pedido, sem se importar se a diferença foi para cima ou para baixo:

SELECT i.pedido_id, p.nome,
       p.preco                                AS catalogo,
       i.preco_unitario                       AS cobrado,
       round(i.preco_unitario - p.preco, 2)   AS diferenca,
       abs(round(i.preco_unitario - p.preco, 2)) AS distancia,
       sign(i.preco_unitario - p.preco)      AS sinal
FROM itens_pedido AS i
JOIN produtos AS p ON p.id = i.produto_id
WHERE i.preco_unitario <> p.preco;

Os três itens vendidos em promoção (os casos de "preço histórico" da loja): todos com sinal -1, ou seja, cobrados abaixo do catálogo.

As funções matemáticas

SELECT sqrt(16)       AS raiz_de_16,
       pow(2, 10)     AS dois_elevado_a_10,
       pi()           AS pi,
       log10(1000)    AS log10_de_1000,
       ln(exp(1))     AS ln_de_e,
       log(2, 8)      AS log_base_2_de_8,
       mod(7.5, 2)    AS mod_com_real;
FunçãoO que fazExemplo → resultado
sqrt(x)Raiz quadrada (square root)sqrt(16) → 4.0; sqrt(-1) → NULL
pow(x, y) ou power(x, y)x elevado a y (power, "potência")pow(2, 10) → 1024.0
exp(x)e (2,718…) elevado a xexp(1) → 2.71828182845905
ln(x)Logaritmo natural (base e)ln(exp(1)) → 1.0
log(x) ou log10(x)Logaritmo na base 10log(1000) → 3.0
log2(x)Logaritmo na base 2log2(8) → 3.0
log(b, x)Logaritmo de x na base blog(2, 8) → 3.0
pi()O número π3.14159265358979
mod(x, y)Resto da divisão, também com números reaismod(7.5, 2) → 1.5
sin, cos, tan, degrees, radians…Trigonometria (ângulos em radianos)degrees(pi()) → 180.0

O mod e o % parecem iguais, mas o % transforma os dois lados em inteiros antes: 7.5 % 2 dá 1.0 (ele calculou 7 % 2), enquanto mod(7.5, 2) dá 1.5.

random(): o sorteio

random() ("aleatório") devolve um número inteiro sorteado, enorme, positivo ou negativo, diferente a cada chamada. Como o resultado muda toda vez, ele aparece aqui numa sessão de terminal da Ana, e não num quadro de resultado:

$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> SELECT random();
-5499406115346342480
sqlite> SELECT random();
-8850731515571685751                 « outro número
sqlite> SELECT abs(random()) % 6 + 1 AS dado;
4
sqlite> SELECT abs(random()) % 6 + 1 AS dado;
1                                    « um dado de 6 faces
sqlite> SELECT nome FROM produtos ORDER BY random() LIMIT 1;
Jogo de Panelas 5 peças
sqlite> SELECT nome FROM produtos ORDER BY random() LIMIT 1;
Jogo de Tabuleiro Trilha Mística     « um produto sorteado
sqlite> .quit
  1. abs(random()) tira o sinal: um número enorme e positivo.
  2. % 6 fica com o resto da divisão por 6: um número de 0 a 5.
  3. + 1 passa para 1 a 6: um dado.

O ORDER BY random() embaralha as linhas: com LIMIT 1, sorteia um produto, por exemplo para o "produto do dia". No editor do guia, random() também funciona, e dá um número diferente a cada Rodar.


6. Os números "quebrados": ponto flutuante

Os números com ponto (o tipo REAL) são guardados em binário, e muitos números que parecem simples para nós, como 0,1, não têm escrita exata em binário, assim como 1/3 não tem escrita exata em decimal (0,3333…). O computador guarda um valor quase igual. Na maioria das vezes ninguém percebe, mas numa comparação aparece:

SELECT 0.1 + 0.2                    AS soma,
       0.1 + 0.2 = 0.3              AS igual_a_0_3,
       (0.1 + 0.2) - 0.3            AS diferenca,
       round(0.1 + 0.2, 2) = 0.3    AS arredondado_igual;

A soma aparece como 0.3 (o SQLite mostra só 15 algarismos), mas a comparação = 0.3 deu 0, falso! A diferença é minúscula, 0,0000000000000000555 (o e-17 quer dizer "vezes 10 elevado a −17"), mas existe. Arredondando antes de comparar, a igualdade volta a ser verdadeira.

Com dinheiro, isso aparece nas somas grandes e nas médias: a média do valor dos itens vendidos é 84.2609090909091, com uma cauda de casas que não são centavos de nada.

SELECT AVG(quantidade * preco_unitario)            AS media_crua,
       round(AVG(quantidade * preco_unitario), 2)  AS media_em_centavos
FROM itens_pedido;
Regras para dinheiro
  • Arredonde com round(x, 2) o resultado final das contas com dinheiro.
  • Nunca compare reais com = sem arredondar antes: round(a, 2) = round(b, 2).
  • Sistemas que precisam de exatidão total (bancos, contabilidade) costumam guardar centavos em inteiros: 12,35 vira 1235. Inteiros são sempre exatos.

7. Formas diferentes de fazer

Quero…UseExemplo com 35 mochilas, caixas de 12
caixas cheias (cortar)estoque / 12 (dois inteiros) ou floor(estoque / 12.0)2 / 2.0
caixas necessárias (para cima)ceil(estoque / 12.0) ou (estoque + 11) / 123.0 / 3
o que sobraestoque % 12 ou mod(estoque, 12)11 / 11.0
a divisão exataestoque / 12.0 ou estoque * 1.0 / 122.91666666666667
arredondar em centavosround(x, 2)—
mostrar com 2 casasprintf('%.2f', x) (texto)—

O (estoque + 11) / 12 é um truque antigo de programador: somar "divisor − 1" antes da divisão inteira faz ela arredondar para cima, e o resultado fica inteiro.


Quando dá errado

As contas quase nunca dão erro no SQLite: elas devolvem um resultado errado, ou NULL. Por isso os casos abaixo merecem atenção.

Divisão por zero
SELECT 7 / 0    AS inteiro,
       7.0 / 0  AS real,
       7 % 0    AS resto;

O que aconteceu: três NULL, sem erro. Uma média com COUNT(*) igual a 0 no divisor dá NULL em silêncio. (No PostgreSQL e no SQL Server, dividir por zero dá erro.)

Como consertar: se o divisor pode ser zero, decida o que mostrar: coalesce(a / b, 0), ou um CASE WHEN b = 0 THEN … END (aula CASE).

A porcentagem que vira zero
SELECT COUNT(*) / 45 * 100     AS pct_errada,
       COUNT(*) * 100.0 / 45   AS pct_certa
FROM produtos
WHERE descontinuado = 1;

O que aconteceu: 2 produtos descontinuados em 45 deveriam dar 4,4%, mas a primeira conta deu 0. Ela faz primeiro 2 / 45, que na divisão inteira é 0, e depois 0 * 100. Duas armadilhas juntas: a divisão inteira e a ordem da conta.

Como consertar: multiplique primeiro e use 100.0: COUNT(*) * 100.0 / 45.

Casas decimais negativas
SELECT round(1234.5678, -2) AS centenas;

O que aconteceu: 1235.0, e não 1200. Em outros bancos, round(x, -2) arredonda para a centena; no SQLite, um número de casas negativo é tratado como 0.

Como consertar: divida, arredonde e multiplique de volta: round(1234.5678 / 100) * 100 dá 1200.0.

Número escrito como texto
SELECT '12.35' * 2   AS texto_numero,
       '3 bananas' + 1 AS texto_com_letras,
       'R$ 12,35' * 2 AS com_moeda;

O que aconteceu: o SQLite tenta transformar o texto em número. '12.35' funciona (24.7). '3 bananas' vira 3, aproveitando só o começo. E 'R$ 12,35' vira 0, porque começa com letra. Nenhum erro.

Como consertar: guarde números em colunas numéricas (INTEGER ou REAL), com ponto e sem símbolo de moeda (aula Os Tipos no SQLite).

Um número grande demais
SELECT abs(-9223372036854775808);

O que aconteceu: integer overflow, "estouro de inteiro". O maior inteiro do SQLite é 9223372036854775807 (cerca de 9 quintilhões), e o menor é −9223372036854775808. O abs do menor não cabe. É um dos poucos erros de conta de verdade; na prática, só aparece com números absurdos (ou com abs(random()), numa chance em 18 quintilhões).

Nos outros bancos
  • Divisão inteira: no PostgreSQL e no SQL Server, 7 / 2 também dá 3, como no SQLite. No MySQL, não: 7 / 2 dá 3.5000, e a divisão inteira é um operador separado, 7 DIV 2.
  • Divisão por zero: o MySQL devolve NULL, como o SQLite; o PostgreSQL e o SQL Server dão erro.
  • log(x) com um argumento: no SQLite e no PostgreSQL é o logaritmo na base 10; no MySQL e no SQL Server é o logaritmo natural. Para não se confundir, use os nomes explícitos log10 e ln (o SQL Server não tem ln: lá, o natural é o LOG).
  • round com metades: o MySQL arredonda 2.5 para 3 em números exatos (DECIMAL), mas um número aproximado como 25E-1 pode ir para 2 (o par mais próximo). Os bancos com servidor têm o tipo DECIMAL/NUMERIC exato, bom para dinheiro; no SQLite, ele vira REAL ou INTEGER.
-- MySQL
SELECT 7 / 2;     -- 3.5000
SELECT 7 DIV 2;   -- 3

Referência rápida

EscritaO que fazExemplo → resultado
a / bDivisão (inteira se os dois forem inteiros)7 / 2 → 3; 7 / 2.0 → 3.5
a % bResto da divisão inteira7 % 2 → 1
abs(x)Valor sem sinalabs(-4.9) → 4.9
sign(x)-1, 0 ou 1sign(-5) → -1
round(x, n)Arredonda em n casas (real); metade vai para longe do zeroround(11.115, 2) → 11.12
ceil(x) / ceiling(x)Para cimaceil(2.1) → 3.0
floor(x)Para baixofloor(2.9) → 2.0
trunc(x)Corta a parte decimaltrunc(-2.9) → -2.0
sqrt(x), pow(x, y), exp(x)Raiz, potência, e elevado a xpow(2, 3) → 8.0
ln(x), log(x)/log10(x), log2(x), log(b, x)Logaritmoslog(100) → 2.0
mod(x, y)Resto, também com reaismod(7.5, 2) → 1.5
pi()π3.14159265358979
random()Inteiro sorteado (muda a cada chamada)abs(random()) % 6 + 1 → um dado
CAST(x AS REAL) / CAST(x AS INTEGER)Converte (para inteiro, corta)CAST(4.9 AS INTEGER) → 4

Exercícios

1. Quanto dá?

No SQLite, quanto vale 9 / 4?

SELECT 9 / 4; -- resultado:

2. Duas casas

Complete para arredondar o preço com desconto em centavos:

SELECT round(preco * 0.9, ) FROM produtos;

3. Qual destas contas dá 3.5 no SQLite?

4. A loja tem 35 mochilas e as guarda em caixas de 12. Qual expressão dá o número de caixas necessárias para guardar todas?

5. O que round(2782.5, 2) mostra no SQLite?

6. Por que 0.1 + 0.2 = 0.3 dá 0 (falso) no SQLite?

Missão final: as caixas da papelaria

Missão: o estoque da papelaria (categoria 1) vai ser guardado em caixas de 12 unidades. A consulta calcula as caixas com estoque / 12, e a Ana descobriu que faltou caixa: as 35 mochilas precisam de 3 caixas, e a conta disse 2. Conserte a coluna caixas para dar o número de caixas necessárias para guardar todo o estoque de cada produto.

SELECT nome, estoque, estoque / 12 AS caixas
FROM produtos
WHERE categoria_id = 1;
SELECT nome, estoque, ceil(estoque / 12.0) AS caixas
FROM produtos
WHERE categoria_id = 1;

Com ceil(estoque / 12.0), as mochilas dão 3.0; com o truque (estoque + 11) / 12, dão 3. As duas respostas estão certas. Repare que ceil(estoque / 12), sem o .0, continua errado: a divisão inteira corta antes do ceil.


Resumo

  • Operadores: + - * / e % (resto). NULL em qualquer conta dá NULL; dividir por zero dá NULL, sem erro.
  • No SQLite, inteiro / inteiro = divisão inteira: 7 / 2 dá 3. Ponha um .0 (7 / 2.0, * 100.0) para a divisão de verdade. Cuidado com médias e porcentagens feitas "na mão".
  • round(x, n) arredonda em n casas e devolve real; ele não completa zeros (2782.5). Para mostrar 2782.50, use printf('%.2f', x).
  • ceil arredonda para cima, floor para baixo, trunc corta; abs tira o sinal e sign diz qual é.
  • Funções matemáticas: sqrt, pow, exp, ln, log (base 10 no SQLite), log2, mod, pi.
  • random() sorteia um inteiro a cada chamada; nunca use em resultados que precisam ser fixos.
  • Números reais são aproximados: 0.1 + 0.2 = 0.3 é falso. Com dinheiro, arredonde com round(x, 2) antes de comparar e de mostrar.