Melhor tipo de dados para armazenar valores monetários no MySQL

279

Eu quero armazenar muitos registros em um banco de dados MySQL. Todos eles contêm valores monetários. Mas não sei quantos dígitos serão inseridos para cada um.
Que tipo de dados eu tenho que usar para esse fim?
VARCHAR ou INT (ou outros tipos de dados numéricos)?

Mohammad Saberi
fonte
13
deimal(10,2)é o que eu uso ... você pode ajustar os valores, dependendo do tamanho esperado
Manse
9
Pergunta relacionada é Melhor tipo de dados para moeda ;).
shA.t

Respostas:

370

Como o dinheiro precisa de uma representação exata, não use tipos de dados que sejam apenas aproximados float. Você pode usar um tipo de dados numérico de ponto fixo para algo como

decimal(15,2)
  • 15 é a precisão (comprimento total do valor, incluindo casas decimais)
  • 2 é o número de dígitos após o ponto decimal

Veja Tipos Numéricos do MySQL :

Esses tipos são usados ​​quando é importante preservar a precisão exata, por exemplo, com dados monetários .

juergen d
fonte
3
qual poderia ser a diferença entre o tipo de dados decimal e numérico para este caso?
Emilio Gort
60
No MySQL decimale numericsão os mesmos.
2141414
21
Eu pessoalmente uso numeric(19,4)para registros financeiros que oferecem uma mão melhor para você jogar e adotar novos pedidos com facilidade.
YahyaE
10
Concordo com YahyaE, mais decimais é melhor. Existem algumas moedas que normalmente usam três casas decimais, como os dinares do Bahrein, da Jordânia ou do Kuwait, portanto, você precisa de pelo menos 3. Quatro ou cinco é melhor.
Edwin Hoogerbeets
1
@EdwinHoogerbeets Não sendo um contador ... mas executando um pequeno negócio no Reino Unido ... Lembro-me de ler em algum lugar há muito tempo que os valores das moedas deveriam ser armazenados em quatro casas decimais, mesmo que £, $, etc., para que certos cálculos pudessem na verdade, use as duas últimas casas decimais para determinados contextos obscuros de contabilidade. Precisamos de um contador para confirmar / refutar.
mike roedor
88

Você pode usar DECIMALou NUMERICambos são iguais

Os tipos DECIMAL e NUMERIC armazenam valores de dados numéricos exatos. Esses tipos são usados ​​quando é importante preservar a precisão exata, por exemplo, com dados monetários. No MySQL, NUMERIC é implementado como DECIMAL, portanto, as seguintes observações sobre DECIMAL se aplicam igualmente a NUMERIC. : MySQL

ie DECIMAL(10,2)

Configurações de exemplo

Boa leitura

NullPoiиteя
fonte
3
Talvez confuso, mas sua captura de tela não corresponde ao texto de resposta (precisão, escala).
Patrick Hofman 14/07
Estou usando decimal (10,2) para o meu valor monetário, no entanto, quando coloco algo como 867.000,00, ele é salvo como 867. o que estou fazendo de errado?
codeinprogress
32

Eu prefiro usar BIGINTe armazenar os valores multiplicando por 100 , para que ele se torne um número inteiro.

Por exemplo, para representar um valor de moeda de 93.49, o valor deve ser armazenado como 9349, ao exibir o valor que podemos dividir por 100 e exibir. Isso ocupará menos espaço de armazenamento.

Cuidado:
Principalmente não realizamos currency * currencymultiplicação; caso contrário, divida o resultado com 100 e armazene, para que ele retorne à precisão adequada.

Dinesh PR
fonte
Lembro-me de ter dito uma coisa semelhante por um professor no meu curso universitário de Sistemas de Computação. Foi-me ensinado que a maneira mais precisa é armazenar em centavos (ou centavo) multiplicando por 100 e salvando como um inteiro e dividindo por 100 para exibi-lo ao usuário. Eu acho que isso traz benefícios em termos de precisão e desempenho do sistema de banco de dados.
LondonAppDev 12/02
11
Qual é a vantagem DECIMAL? Você cria a necessidade de converter moedas de um centavo para dólares, e ai se você a esquecer em algum momento.
1
O espaço é a única vantagem, mas sim, precisamos ter mais cuidado ao usar esse recurso.
Dinesh PR
4
Caso isso não seja óbvio: tenha cuidado ao usar o método de remoção da balança se você guardar dinheiro em centavos fracionários (por exemplo, $0.005ou $0.12345) porque eles não serão reduzidos a um número inteiro depois de multiplicados por 100. Se você souber a precisão dos valores, desmarque a caixa A melhor opção é usar DECIMAL. Mas se você não souber a precisão (como nos meus exemplos), então… seria FLOATapropriado?
Quinn Comendant
1
Uma vantagem desse método ocorre ao usar uma linguagem como JavaScript que usa o IEEE-754 para armazenar números de ponto flutuante. Esta especificação não garante que 0,1 + 0,2 === 0,3 seja verdadeiro. Armazenar a moeda como um número inteiro garante que o seu aplicativo não incorra nesse tipo de erro. Esta pode não ser a melhor solução. Cheguei a esta página enquanto pesquisava soluções e ainda não terminei.
Gary Ott
27

Depende da sua necessidade.

Usar DECIMAL(10,2)normalmente é suficiente, mas se você precisar de valores um pouco mais precisos, poderá definir DECIMAL(10,4).

Se você trabalha com grandes valores, substitua 10por 19.

Svetoslav
fonte
Estou usando decimal (10,2) para o meu valor monetário, no entanto, quando coloco algo como 867.000,00, ele é salvo como 867. o que estou fazendo de errado?
codeinprogress
2
@codeinprogress Utilizando o separador local / decimal errado?
David Balažic 30/03
15

Se o seu aplicativo precisar lidar com valores monetários de até um trilhão, isso deve funcionar: 13,2 Se você precisar cumprir os GAAP (princípios de contabilidade geralmente aceitos), use: 13,4

Normalmente, você deve somar seus valores monetários em 13,4 antes de arredondar a saída para 13,2.

david.ee
fonte
5
Se você estiver indo para tomar Bitcoin, você vai precisar de 8 casas decimais, embora a maioria das carteiras ir para MBTC que é 3 en.wikipedia.org/wiki/Bitcoin
Christian
Não pense que essa resposta é verdadeira. opendata.stackexchange.com/a/10348/13983 @ david.ee tem uma fonte para isso?
Evan Carroll
@EvanCarroll, deixe-me responder por david.ee. Acho que este artigo pode ser a fonte rietta.com/blog/2012/03/03/best-data-types-for-currencymoney-in
NAXA
@naXa, o link não cita nada de nenhuma fonte que suporte a reivindicação de uso de 13,4 para GAAP. Tudo o que você fez foi o link para um artigo que faz a mesma reivindicação sem fundamento.
Iheanyi
6

De fato, isso depende das preferências do programador. Eu pessoalmente uso: numeric(15,4)para estar em conformidade com os princípios contábeis geralmente aceitos ( GAAP ) .

Chagbert
fonte
5
Não tem nada a ver com "preferências do programador" ou com o que você 'usa pessoalmente'. É ditado pelo domínio do problema, que requer uma raiz decimal. Não é uma questão em que o programador exerça suas próprias preferências pessoais.
Marquês de Lorne
3

Tente usar

Decimal(19,4)

isso geralmente funciona com todos os outros bancos de dados também

Deepesh
fonte
3

Nós usamos double.

*suspiro*

Por quê?

Porque ele pode representar qualquer número de 15 dígitos sem restrições sobre onde está o ponto decimal . Tudo por um mísero 8 bytes!

Portanto, pode representar:

  • 0.123456789012345
  • 123456789012345.0

... e qualquer coisa no meio.

Isso é útil porque estamos lidando com moedas globais e doublepode armazenar os vários números de casas decimais que provavelmente encontraremos.

Um único doublecampo pode representar 999.999.999.999.999s em ienes japoneses, 9.999.999.999.999.999s em dólares americanos e até 9.999.999.9999999999s em bitcoins

Se você tentar fazer o mesmo decimal, precisará de um decimal(30, 15)custo de 14 bytes.

Ressalvas

Claro, o uso doublenão é sem ressalvas.

No entanto, não é perda de precisão, como alguns tendem a apontar. Mesmo que doubleele próprio não seja internamente exato para o sistema base 10 , podemos torná-lo exato arredondando o valor que extraímos do banco de dados para suas casas decimais significativas. Se necessário, é isso. (por exemplo, se for produzido, e a representação da base 10 é necessária.)

As advertências são: sempre que executamos aritmética com ele, precisamos normalizar o resultado (arredondando-o para suas casas decimais significativas) antes de:

  1. Realizando comparações.
  2. Escrevendo de volta no banco de dados.

Outro tipo de ressalva é que, diferentemente de decimal(m, d)onde o banco de dados impedirá que os programas insiram um número com mais de mdígitos, essas validações não existem double. Um programa pode inserir um valor digitado pelo usuário de 20 dígitos e acabará sendo gravado silenciosamente como uma quantidade imprecisa.

Antak
fonte
Primeira vez que vi uma resposta como essa, interessante. P: Se eu escrever um float como 1,41 no banco de dados e, por algum motivo, preciso multiplicá-lo por um número enorme no mysql, como 1.000.000.000.000. O resultado arredondado será exatamente: 1.410.000.000.000?
roelleor
@roelleor Para que o resultado seja exatamente 1.410.000.000.000 (vírgula como separador de milhares), a entrada é assumida como sendo 1.410000000000(doze casas decimais significativas), mas multiplicando isso por 1.000.000.000.000 (que são 13 dígitos significativos à esquerda do ponto decimal) significa que estamos trabalhando em menos 25 dígitos combinados de significância. Isso supera em muito os 15 disponíveis para um duplo, então, em termos de design, acho que seria muito quebrado.
Antak 20/05/19
2

Na época, essa pergunta foi feita e ninguém pensou no preço do Bitcoin. No caso do BTC, provavelmente é insuficiente para usar DECIMAL(15,2). Se o Bitcoin subir para US $ 100.000 ou mais, precisaremos pelo menos DECIMAL(18,9)oferecer suporte a criptomoedas em nossos aplicativos.

DECIMAL(18,9)ocupa 12 bytes de espaço no MySQL ( 4 bytes por 9 dígitos ).

bizwiz
fonte
> Um bitcoin pode ser dividido em 8 casas decimais. Portanto, 0,00000001 BTC é o menor valor que pode ser tratado em uma transação. Eu acho que você quer dizer 8 em vez de 9?
danger89
1
Eu sei, mas 9 leva o mesmo espaço em disco como 8. De docs MySQL: "Os valores para colunas decimais são armazenados usando um formato binário que embala nove dígitos decimais em 4 bytes"
BizWiz
Desculpe, agora eu entendo você. Obrigado.
danger89
2

Armazenar dinheiro BIGINTmultiplicado por 100 ou mais com o motivo de usar menos espaço de armazenamento não faz sentido em todas as situações "normais".

  • Para se manter alinhado com o GAAP, é suficiente armazenar moedas em DECIMAL(13,4)
  • O manual do MySQL lê que ele precisa de 4 bytes por 9 dígitos para armazenar DECIMAL.
  • DECIMAL(13,4) representa 9 dígitos + 4 dígitos da fração (casas decimais) => 4 + 2 bytes = 6 bytes
  • compare com 8 bytes necessários para armazenar BIGINT.
digitalstraw
fonte