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,
ROUND(3.14159, 0) AS pi0,
ROUND(2.5, 0) AS half;
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,
ROUND(12345.678, -2) AS hundreds,
ROUND(12345.678, -3) AS thousands;
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:
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.
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,
roundBankers(3.5) AS rb;
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.
ROUND(x, n)arredonda o numeroxparancasas decimais. Parece trivial, mas e exatamente aqui que as pessoas se machucam: no PostgreSQL a forma de dois argumentos so funciona comnumeric, nao comdouble 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
numericno PostgreSQL.SELECT ROUND(3.14159, 2) AS pi2, -- 3.14 ROUND(3.14159, 0) AS pi0, -- 3 ROUND(2.5, 0) AS half; -- 3Propriedades principais:
nvale0por padrao, entaoROUND(x)arredonda para um inteiro.numerice a escala pedida:ROUND(2, 2)retorna2.00, nao2.TRUNC(3.99, 1)da3.9, enquantoROUND(3.99, 1)da4.0.n negativo: arredondar a esquerda do ponto decimal
O recurso mais subestimado e um
nnegativo. 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; -- 12000Na 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 temround(double precision, integer). Se a coluna fordouble precisionoureal, 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.0produzdouble precision. A correcao e um cast paranumeric: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 comonumeric(12, 2)desde o inicio e voce nunca vai precisar do cast.Formatando moeda
ROUNDleva o numero em si a escala certa, mas nao adiciona um simbolo de moeda, nao agrupa milhares nem mantem zeros finais em umdouble precision. Para uma saida bonita, useto_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
FM999G999D00remove os espacos de preenchimento (FM), coloca um separador de milhares (G) e fixa duas casas decimais (D00). Arredonde para o calculo e formate comto_charpara 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.ROUNDem tipos inteiros e decimais sem cast, e umnnegativo tambem funciona. Para um formato de dinheiro com separadores, useFORMAT(x, 2).SELECT FORMAT(ROUND(amount, 2), 2) AS pretty_total FROM orders;roundarredonda a metade para o par, enquantoroundBankerstorna 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; -- 4Lembre-se de uma coisa:
ROUNDcontrola a precisao de um numero, nao a aparencia dele, e no PostgreSQL o segundo argumento quase sempre precisa denumeric.