sqlpostgresqlroundnumeric

ROUND(x, n) no SQL: arredondar casas decimais, n negativo e formatar moeda

Como ROUND(x, n) arredonda para n casas decimais, o que um n negativo faz e por que o PostgreSQL exige numeric em vez de double precision na forma de dois argumentos.

2 min de leituraReferencesql · postgresql · round · numeric · currency · clickhouse

ROUND(x, n) arredonda o numero x para n casas decimais. Parece trivial, mas e exatamente aqui que as pessoas se machucam: no PostgreSQL a forma de dois argumentos so funciona com numeric, nao com double precision, e a consulta falha do nada.

O que o ROUND(x, n) faz

O primeiro argumento e o numero, o segundo e quantas casas decimais manter. Ele arredonda para o valor mais proximo; os empates se afastam de zero para numeric no PostgreSQL.

SELECT
  ROUND(3.14159, 2) AS pi2,   -- 3.14
  ROUND(3.14159, 0) AS pi0,   -- 3
  ROUND(2.5,     0) AS half;  -- 3

Propriedades principais:

  • n vale 0 por padrao, entao ROUND(x) arredonda para um inteiro.
  • O resultado mantem o tipo numeric e a escala pedida: ROUND(2, 2) retorna 2.00, nao 2.
  • Ele arredonda, nao trunca: TRUNC(3.99, 1) da 3.9, enquanto ROUND(3.99, 1) da 4.0.

n negativo: arredondar a esquerda do ponto decimal

O recurso mais subestimado e um n negativo. Ele arredonda a esquerda do ponto decimal: para dezenas, centenas, milhares. E pratico para agrupar valores em baldes.

SELECT
  ROUND(12345.678, -1) AS tens,       -- 12350
  ROUND(12345.678, -2) AS hundreds,   -- 12300
  ROUND(12345.678, -3) AS thousands;  -- 12000

Na pratica, isso permite montar um histograma de valores de pedidos sem uma tabela de faixas separada:

SELECT
  ROUND(amount, -2) AS bucket,
  COUNT(*)          AS orders_in_bucket
FROM orders
WHERE status = 'paid'
GROUP BY ROUND(amount, -2)
ORDER BY bucket;

Cada pedido cai em um balde de largura 100 e voce ve na hora a distribuicao dos valores.

numeric vs double precision: a grande pegadinha

Aqui esta o verdadeiro obstaculo. O PostgreSQL tem round(numeric, integer) mas nao tem round(double precision, integer). Se a coluna for double precision ou real, a chamada de dois argumentos nao encontra uma funcao compativel:

-- ERROR: function round(double precision, integer) does not exist
SELECT ROUND(salary / 12.0, 2) FROM employees;

Aqui salary / 12.0 produz double precision. A correcao e um cast para numeric:

SELECT
  name,
  ROUND(salary::numeric / 12, 2) AS monthly_pay
FROM employees;

O ruim e que a forma de um argumento ROUND(double precision) existe e funciona, entao o erro fica escondido ate alguem adicionar um segundo argumento. Regra simples: guarde dinheiro como numeric(12, 2) desde o inicio e voce nunca vai precisar do cast.

Formatando moeda

ROUND leva o numero em si a escala certa, mas nao adiciona um simbolo de moeda, nao agrupa milhares nem mantem zeros finais em um double precision. Para uma saida bonita, use to_char.

SELECT
  u.name,
  SUM(o.amount)                              AS raw_total,
  ROUND(SUM(o.amount), 2)                     AS rounded_total,
  to_char(ROUND(SUM(o.amount), 2), 'FM999G999D00') AS pretty_total
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE o.status = 'paid'
GROUP BY u.name;

A mascara FM999G999D00 remove os espacos de preenchimento (FM), coloca um separador de milhares (G) e fixa duas casas decimais (D00). Arredonde para o calculo e formate com to_char para exibir: nao misture as duas tarefas em uma unica expressao.

Diferencas no MySQL e ClickHouse

A ideia do ROUND(x, n) e a mesma em todo lugar, mas os detalhes importam.

  • MySQL aceita ROUND em tipos inteiros e decimais sem cast, e um n negativo tambem funciona. Para um formato de dinheiro com separadores, use FORMAT(x, 2).
SELECT FORMAT(ROUND(amount, 2), 2) AS pretty_total
FROM orders;
  • ClickHouse separa o arredondamento bancario do habitual: round arredonda a metade para o par, enquanto roundBankers torna isso explicito. Se voce espera o classico "metade para cima", lembre dessa diferenca.
SELECT
  round(2.5)        AS r,   -- 2 (half to even)
  roundBankers(3.5) AS rb;  -- 4

Lembre-se de uma coisa: ROUND controla a precisao de um numero, nao a aparencia dele, e no PostgreSQL o segundo argumento quase sempre precisa de numeric.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador