Banco de dúvidas

Armazenando valores monetários no MySQL

Para sistemas que lidam com valores monetários, é importante a escolha adequada do tipo de dado para armazená-los no MySQL, garantindo sua precisão, integridade e conformidade com GAAP (Generally Accepted Accounting Principles, ou princípios de contabilidade geralmente aceitos).

O perigo do ponto flutuante

Representados pelos tipos FLOAT ou DOUBLE, o uso de ponto flutuante é problemático dada a natureza imprecisa da representação, erros de arredondamento podem ocorrer ao realizar operações matemáticas, resultando em cálculos incorretos e discrepâncias nos registros financeiros.

Por exemplo, se temos uma tabela usando DOUBLE para armazenar dinheiro:

create table dinheiro (
   id serial,
   dinheiro_1 double,
   dinheiro_2 double,
);

... e inserirmos valores monetários:

insert into
   dinheiro (dinheiro_1, dinheiro_2)
values
   (100.4, 20.4),
   (-80.0, 0.0);

A soma dos valores de cada coluna deveria retornar o mesmo valor (100,4 - 80,0 deve ser igual a 20,4 + 0,0). Porém, ao solicitar a soma das colunas:

select sum(dinheiro_1), sum(dinheiro_2) from dinheiro;

... veja o resultado:

SUM(dinheiro_1) SUM(dinheiro_2)
20.400000000000006 20.4

Mesmo a diferença sendo extremamente pequena, os valores são diferentes para fins de comparação exata. Erros de arredondamento como esse podem se acumular em somas de vários valores, criando diferenças significativas.

Sempre use DECIMAL(10, 2) ou NUMERIC(10, 2)

Para evitar essa situação, recomenda-se o uso dos tipos DECIMAL ou NUMERIC (ambos têm o mesmo significado no MySQL), especificando pelo menos duas casas decimais. Esses tipos preservam a precisão de forma exata, importante para valores monetários. Por exemplo, DECIMAL(10, 2) significa que o campo pode armazenar números com até 10 dígitos, sendo 2 deles reservados para casas decimais.

ATENÇÃO: sempre especifique o número de casas decimais. Se usar apenas DECIMAL ou NUMERIC ao criar tabelas, nenhuma casa decimal será alocada, o que é inadequado para valores monetários.

Precisão para conformidade com GAAP

A conformidade com GAAP exige alta precisão nos cálculos financeiros. Nesse caso, recomenda-se DECIMAL(15, 4), para que, mesmo lidando com uma grande quantidade de operações, erros de arredondamento sejam considerados imateriais.

Bitcoin e outras criptomoedas

Moedas digitais como o Bitcoin frequentemente envolvem valores com várias casas decimais, exigindo alta precisão de armazenamento. Por isso, valores de Bitcoin devem usar DECIMAL(16, 8), para poder representar o satoshi (a menor quantidade de Bitcoin, que equivale a um centésimo de milionésimo do seu valor: 0.00000001 BTC) e ainda ter oito casas decimais restantes para a quantidade máxima de Bitcoins, que nunca ultrapassará 21 milhões.

Para outras criptomoedas, as exigências de armazenamento e precisão podem ser diferentes.

Este artigo foi útil para você?