Pergunta

Estou usando uma coluna decimal para armazenar valores de dinheiro em um banco de dados e hoje eu estava me perguntando qual precisão e escala usar.

Como as colunas supostamente char de uma largura fixa são mais eficientes, eu pensava que o mesmo poderia ser verdadeiro para colunas decimais. É isso?

E que precisão e escala devo usar? Eu estava pensando em precisão 24/8. Isso é exagerado, não é suficiente ou ok?


Foi isso que eu decidi fazer:

  • Armazene as taxas de conversão (quando aplicável) na própria tabela de transações, como um flutuador
  • Armazene a moeda na tabela de conta
  • O valor da transação será um DECIMAL(19,4)
  • Todos os cálculos usando uma taxa de conversão serão tratados pelo meu aplicativo, para que eu mantenho o controle de problemas de arredondamento

Eu não acho que um flutuador para a taxa de conversão seja um problema, pois é principalmente para referência, e eu o lançarei para um decimal de qualquer maneira.

Obrigado a todos pela sua valiosa contribuição.

Foi útil?

Solução

Se você está procurando um tamanho único, eu sugiro DECIMAL(19, 4) é uma escolha popular (um rápido que o Google apresenta isso). Eu acho que isso se origina do tipo de dados antigo de VBA/acesso/moeda de jato, sendo o primeiro tipo decimal de ponto fixo no idioma; Decimal Somente veio no estilo 'versão 1.0' (ou seja, não totalmente implementado) no VB6/VBA6/JET 4.0.

A regra prática para armazenar de valores decimais de ponto fixo é armazenar pelo menos mais um local decimal do que você realmente precisa para permitir o arredondamento. Uma das razões para mapear o antigo Currency digite o front -end para DECIMAL(19, 4) Digite no back -end foi que Currency exibiram arredondamento de banqueiros por natureza, enquanto DECIMAL(p, s) arredondado por truncamento.

Um lugar decimal extra em armazenamento para DECIMAL Permite que um algoritmo de arredondamento personalizado seja implementado, em vez de pegar o padrão do fornecedor (e o arredondamento dos banqueiros é alarmante, para dizer o mínimo, para um designer que espera que todos os valores que terminem em 0,5 se aproximem de zero).

Sim, DECIMAL(24, 8) Parece um exagero para mim. A maioria das moedas é citada em quatro ou cinco casas decimais. Eu conheço situações em que uma escala decimal de 8 (ou mais) é Necessário, mas é aqui que uma quantidade monetária "normal" (digamos quatro casas decimais) foi proposta, o que implica que a precisão decimal deve ser reduzida de acordo (considere também um tipo de ponto flutuante em tais circunstâncias). E ninguém tem muito dinheiro hoje em dia para exigir uma precisão decimal de 24 :)

No entanto, em vez de uma abordagem de tamanho único, algumas pesquisas podem estar em ordem. Pergunte ao seu designer ou especialista em domínio sobre regras contábeis que podem ser aplicáveis: GAAP, UE, etc. Lembro-me vagamente de algumas transferências intra-estatais da UE com regras explícitas para arredondar para cinco lugares decimais, portanto, usando, portanto, usando DECIMAL(p, 6) para armazenamento. Os contadores geralmente parecem favorecer quatro lugares decimais.


PS Evite o SQL Server's MONEY Tipo de dados porque tem problemas sérios com precisão ao arredondar, entre outras considerações, como portabilidade etc. Veja Blog de Aaron Bertrand.


Os designers da Microsoft e do idioma escolheram o arredondamento do Banker porque os designers de hardware o escolheram [citação?]. Está consagrado nos padrões do Instituto de Engenheiros Elétricos e Eletrônicos (IEEE), por exemplo. E os designers de hardware escolheram porque os matemáticos preferem. Ver Wikipedia; Parafraseando: a edição de 1906 da probabilidade e teoria dos erros chamou essa 'regra do computador' ("computadores", significando humanos que realizam cálculos).

Outras dicas

Recentemente, implementamos um sistema que precisa lidar com valores em várias moedas e converter entre eles e descobrimos algumas coisas da maneira mais difícil.

Nunca use números de ponto flutuante para dinheiro

A aritmética do ponto flutuante introduz imprecisões que não podem ser notadas até que elas estranhem algo. Todos os valores devem ser armazenados como números inteiros ou tipos de decimal fixo e, se você optar por usar um tipo de decimal fixo, certifique-se de entender exatamente o que esse tipo faz sob o capô (ou seja, ele usa internamente um número inteiro ou ponto flutuante modelo).

Quando você precisa fazer cálculos ou conversões:

  1. Converter valores em ponto flutuante
  2. Calcule novo valor
  3. Arredondar o número e convertê -lo de volta a um número inteiro

Ao converter um número de ponto flutuante de volta para um número inteiro na etapa 3, não basta lançá -lo - use uma função de matemática para contorná -lo primeiro. Isso geralmente será round, embora em casos especiais possa ser floor ou ceil. Saiba a diferença e escolha com cuidado.

Armazene o tipo de um número ao lado do valor

Isso pode não ser tão importante para você se você estiver lidando apenas com uma moeda, mas foi importante para lidar com várias moedas. Utilizamos o código de 3 caracteres para uma moeda, como USD, GBP, JPY, EUR, etc.

Dependendo da situação, também pode ser útil armazenar:

  • Se o número é antes ou depois do imposto (e qual era a taxa de imposto)
  • Se o número é o resultado de uma conversão (e do que foi convertido)

Conheça os limites de precisão dos números com os quais você está lidando

Para valores reais, você deseja ser tão preciso quanto a menor unidade da moeda. Isso significa que você não tem valores menores que um centavo, um centavo, um iene, um fen, etc. Não armazene valores com maior precisão do que isso sem motivo.

Internamente, você pode optar por lidar com valores menores; nesse caso, isso é um tipo diferente de valor de moeda. Certifique -se de que seu código saiba qual é qual e não os confunde. Evite usar valores de ponto flutuante mesmo aqui.


Adicionando todas essas regras, decidimos as seguintes regras. Na execução do código, as moedas são armazenadas usando um número inteiro para a menor unidade.

class Currency {
   String code;       //  eg "USD"
   int value;         //  eg 2500
   boolean converted;
}

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}

No banco de dados, os valores são armazenados como uma string no seguinte formato:

USD:2500

Isso armazena o valor de US $ 25,00. Conseguimos fazer isso apenas porque o código que lida com moedas não precisa estar dentro da própria camada de banco de dados, para que todos os valores possam ser convertidos primeiro em memória. Outras situações sem dúvida se prestam a outras soluções.


E caso eu não tenha deixado claro antes, Não use float!

Ao lidar com dinheiro no MySQL, use decimal (13,2) se você souber a precisão de seus valores de dinheiro ou use o dobro se quiser apenas um valor aproximado aproximado rápido. Portanto, se o seu aplicativo precisar lidar com valores de dinheiro até um trilhão de dólares (ou euros ou libras), isso deve funcionar:

DECIMAL(13, 2)

Ou, se você precisar cumprir com Gaap Em seguida, use:

DECIMAL(13, 4)

4 lugares decimais dariam a você a precisão para armazenar as menores subunidades de moeda do mundo. Você pode derrubá -lo ainda mais se precisar de uma precisão do micropagamento (nanopayment?!).

Eu também prefiro DECIMAL Para os tipos de dinheiro específicos para DBMS, você está mais seguro mantendo esse tipo de lógica no aplicativo IMO. Outra abordagem ao longo da mesma linha é simplesmente usar um número inteiro [longo], com a formatação em ¤unit.Subunit para a legibilidade humana (¤ = símbolo da moeda) feita no nível do aplicativo.

O tipo de dados do dinheiro no SQL Server possui quatro dígitos após o decimal.

Do SQL Server 2000 Books Online:

Os dados monetários representam quantias positivas ou negativas de dinheiro. No Microsoft® SQL Server ™ 2000, os dados monetários são armazenados usando os tipos de dados de dinheiro e dinheiro pequeno. Os dados monetários podem ser armazenados com precisão de quatro casas decimais. Use o tipo de dados do dinheiro para armazenar valores na faixa de -922.337.203.685.477.5808 a +922.337.203.685.477.5807 (requer 8 bytes para armazenar um valor). Use o tipo de dados SmallMoney para armazenar valores na faixa de -214.748.3648 a 214.748.3647 (exige que 4 bytes armazenem um valor). Se for necessário um número maior de locais decimais, use o tipo de dados decimal.

Às vezes, você precisará ir para menos de um centavo e há moedas internacionais que usam demoniações muito grandes. Por exemplo, você pode cobrar seus clientes 0,088 centavos por transação. No meu banco de dados Oracle, as colunas são definidas como número (20,4)

Se você estiver fazendo algum tipo de operações aritméticas no banco de dados (multiplicando as taxas de cobrança e assim por diante), provavelmente você vai querer muito mais precisão do que as pessoas aqui estão sugerindo, pelas mesmas razões que você nunca Deseja usar algo menos que um valor de ponto flutuante de precisão dupla no código do aplicativo.

Se você estivesse usando o IBM Informix Dynamic Server, teria um tipo de dinheiro que é uma variante menor no tipo decimal ou numérico. É sempre um tipo de ponto fixo (enquanto decimal pode ser do tipo de ponto flutuante). Você pode especificar uma escala de 1 a 32 e uma precisão de 0 a 32 (inadimplente para uma escala de 16 e uma precisão de 2). Portanto, dependendo do que você precisa armazenar, você pode usar decimal (16,2) - ainda grande o suficiente para manter o déficit federal dos EUA, até o centavo mais próximo - ou você pode usar uma faixa menor ou mais casas decimais.

Eu pensaria que, em grande parte, os requisitos de seus clientes ou de seu cliente devem ditar qual precisão e escala usar. Por exemplo, para o site de comércio eletrônico em que estou trabalhando com o dinheiro apenas em GBP, fui obrigado a mantê-lo decimal (6, 2).

Uma resposta tardia aqui, mas eu usei

DECIMAL(13,2)

O que estou pensando em permitir até 99.999.999.999,99.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top