Almacenar dinero en una columna decimal - ¿qué precisión y escala?


Estoy usando una columna decimal para almacenar valores monetarios en una base de datos, y hoy me preguntaba qué precisión y escala usar.

Dado que supuestamente las columnas char de un ancho fijo son más eficientes, estaba pensando que lo mismo podría ser cierto para las columnas decimales. Es?

¿Y qué precisión y escala debo usar? Estaba pensando en precisión 24/8. ¿Es exagerado, no es suficiente o está bien?


Esto es lo que he decidido hacer:

  • Almacenar las tasas de conversión (cuando aplicable) en la propia tabla de transacciones, como flotador
  • Almacenar la moneda en la tabla de cuentas
  • El importe de la transacción será un DECIMAL(19,4)
  • Todos los cálculos que utilizan una tasa de conversión serán manejados por mi aplicación, por lo que mantengo el control de los problemas de redondeo

No creo que un flotador para la tasa de conversión sea un problema, ya que es principalmente para referencia, y lo voy a lanzar a un decimal de todos modos.

Gracias a todos por su valiosa aportación.

Author: Nick Chammas, 2008-10-22

10 answers

Si usted está buscando una talla única para todos, yo sugeriría DECIMAL(19, 4) es una opción popular (un rápido Google confirma esto). Creo que esto se origina en el antiguo tipo de datos de moneda VBA/Access / Jet, siendo el primer tipo decimal de punto fijo en el lenguaje; Decimal solo vino en el estilo 'versión 1.0' (es decir, no implementado completamente) en VB6/VBA6/Jet 4.0.

La regla general para el almacenamiento de valores decimales de punto fijo es almacenar al menos un lugar decimal más de lo que realmente necesita para permitir el redondeo. Una de las razones para mapear el antiguo tipo Currency en el front-end con el tipo DECIMAL(19, 4) en el back-end fue que Currency mostraba el redondeo de los banqueros por naturaleza, mientras que DECIMAL(p, s) redondeado por truncamiento.

Un lugar decimal adicional en el almacenamiento para DECIMAL permite implementar un algoritmo de redondeo personalizado en lugar de tomar el valor predeterminado del proveedor (y el redondeo de los banqueros es alarmante, por decir lo menos, para un diseñador que espera que todos los valores terminen en .5 para redondear lejos de cero).

Sí, DECIMAL(24, 8) me suena a exageración. La mayoría de las monedas se cotizan a cuatro o cinco decimales. Sé de situaciones en las que se requiere una escala decimal de 8 (o más) , pero aquí es donde se ha prorrateado una cantidad monetaria "normal" (digamos cuatro lugares decimales), lo que implica que la precisión decimal debe reducirse en consecuencia (también considere un tipo de coma flotante en tales circunstancias). Y nadie tiene tanto dinero hoy en día para requerir una precisión decimal de 24 :)

Sin embargo, en lugar de un enfoque único para todos, algunas investigaciones pueden estar en orden. Pregunte a su diseñador o experto de dominio sobre las normas contables que pueden ser aplicables: GAAP, UE, etc. Recuerdo vagamente algunas transferencias intraestatales de la UE con normas explícitas para redondear a cinco decimales, por lo tanto utilizando DECIMAL(p, 6) para el almacenamiento. Por lo general, los contables parecen estar a favor de cuatro decimales.


PS Evite el tipo de datos de SQL Server MONEY porque tiene serios problemas de precisión al redondear, entre otras consideraciones como la portabilidad, etc. Ver El blog de Aaron Bertrand.


Microsoft y los diseñadores de lenguaje eligieron el redondeo de banker porque los diseñadores de hardware lo eligieron [citation?]. Está consagrado en las normas del Instituto de Ingenieros Eléctricos y Electrónicos (IEEE), por ejemplo. Y los diseñadores de hardware lo eligieron porque los matemáticos lo prefieren. Ver Wikipedia ; parafraseando: La edición de 1906 de Probabilidad y Teoría de Errores llamada a esto' la regla de la computadora ' ("computadoras" significa humanos que realizan cálculos).

 145
Author: onedaywhen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-09 09:39:56

Recientemente implementamos un sistema que necesita manejar valores en múltiples monedas y convertir entre ellas, y descubrimos algunas cosas de la manera difícil.

NUNCA USE NÚMEROS DE COMA FLOTANTE PARA DINERO

La aritmética de coma flotante introduce inexactitudes que pueden no notarse hasta que hayan arruinado algo. Todos los valores deben almacenarse como enteros o tipos decimales fijos, y si elige usar un tipo decimal fijo, asegúrese de comprender exactamente lo que hace ese tipo bajo el capó (es decir, utiliza internamente un tipo entero o de coma flotante).

Cuando necesitas hacer cálculos o conversiones:

  1. Convertir valores a coma flotante
  2. Calcular nuevo valor
  3. Redondee el número y conviértalo de nuevo en un entero

Al convertir un número de coma flotante de nuevo a un entero en el paso 3, no se limite a convertirlo - utilice una función matemática para redondearlo primero. Esto generalmente será round, aunque en casos especiales podría ser floor o ceil. Conozca la diferencia y elija cuidadosamente.

Almacene el tipo de un número junto al valor

Esto puede no ser tan importante para usted si solo está manejando una moneda, pero fue importante para nosotros en el manejo de varias monedas. Utilizamos el código de 3 caracteres para una moneda, como USD, GBP,JPY, EUR, etc.

Dependiendo de la situación, también puede ser útil almacenar:

  • Si el número es antes o después de impuestos (y cuál era la tasa impositiva)
  • Si el número es el resultado de una conversión (y de qué se convirtió)

Conoce los límites de precisión de los números con los que estás tratando

Para los valores reales, desea ser tan preciso como la unidad más pequeña de la moneda. Esto significa que no tiene valores más pequeños que un centavo, un centavo, un yen, un fen, etc. No almacene valores con mayor precisión que eso sin ninguna razón.

Internamente, usted puede optar por tratar con valores más pequeños, en cuyo caso es un tipo diferente de valor de moneda. Asegúrate de que tu código sabe cuál es cuál y no los confunde. Evite usar valores de coma flotante incluso aquí.


Sumando todas esas reglas, decidimos las siguientes reglas. En el código en ejecución, las monedas se almacenan utilizando un entero para la unidad más pequeña.

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

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

En la base de datos, los valores se almacenan como una cadena en el siguiente formato:

USD:2500

Que almacena el valor de $25.00. Pudimos hacer eso solo porque el código que trata con monedas no necesita estar dentro de la capa de base de datos en sí, por lo que todos los valores se pueden convertir en memoria primero. Otras situaciones sin duda se prestarán a otras soluciones.


Y en caso de que no lo aclare antes, ¡no uses float!

 86
Author: Marcus Downing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-22 12:47:39

Cuando maneje dinero en MySQL, use DECIMAL(13,2) si conoce la precisión de sus valores de dinero o use DOUBLE si solo desea un valor aproximado rápido y suficiente. Así que si su aplicación necesita manejar valores de dinero de hasta un billón de dólares (o euros o libras), entonces esto debería funcionar:

DECIMAL(13, 2)

O, si necesita cumplir con GAAP entonces use:

DECIMAL(13, 4)
 3
Author: pollux1er,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-20 14:15:28

4 decimales le darían la precisión para almacenar las subunidades de moneda más pequeñas del mundo. Usted puede tomarlo abajo más lejos si usted necesita micropago(nanopayment?!) exactitud.

Yo también prefiero DECIMAL a los tipos de dinero específicos de DBMS, es más seguro mantener ese tipo de lógica en la aplicación IMO. Otro enfoque en la misma línea es simplemente usar un entero [largo], con formato en ¤unit.subunidad para legibilidad humana (¤=símbolo de moneda) realizada a nivel de aplicación.

 2
Author: bobince,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-02 00:09:13

El tipo de datos money en SQL Server tiene cuatro dígitos después del decimal.

De SQL Server 2000 Libros en Línea:

Los datos monetarios representan cantidades de dinero positivas o negativas. En Microsoft ® SQL Server™ 2000, los datos monetarios se almacenan utilizando los tipos de datos money y smallmoney. Los datos monetarios se pueden almacenar con una precisión de cuatro decimales. Utilice el tipo de datos money para almacenar valores en el rango de -922,337,203,685,477.5808 a +922,337,203,685,477. 5807 (requiere 8 bytes para almacenar un valor). Utilice el tipo de datos smallmoney para almacenar valores en el rango de -214,748.3648 a 214,748.3647 (requiere 4 bytes para almacenar un valor). Si se requiere un mayor número de lugares decimales, utilice el tipo de datos decimal en su lugar.

 1
Author: Austin Salonen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-22 04:10:49

A veces tendrá que ir a menos de un centavo y hay monedas internacionales que utilizan demoniaciones muy grandes. Por ejemplo, puede cobrar a sus clientes 0.088 centavos por transacción. En mi base de datos Oracle las columnas se definen como NÚMERO (20,4)

 1
Author: WW.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-22 06:11:10

Si vas a hacer cualquier tipo de operaciones aritméticas en la base de datos (multiplicando las tasas de facturación y así sucesivamente), probablemente querrás mucha más precisión de la que la gente aquí está sugiriendo, por las mismas razones que nunca querrías usar nada menos que un valor de punto flotante de doble precisión en el código de la aplicación.

 1
Author: Hank Gay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-22 11:34:07

Si estuviera utilizando IBM Informix Dynamic Server, tendría un tipo de DINERO que es una variante menor en el tipo DECIMAL o NUMÉRICO. Siempre es un tipo de punto fijo (mientras que el DECIMAL puede ser un tipo de punto flotante). Puede especificar una escala del 1 al 32 y una precisión del 0 al 32 (por defecto, una escala de 16 y una precisión de 2). Por lo tanto, dependiendo de lo que necesite almacenar, puede usar DECIMAL (16,2), aún lo suficientemente grande como para mantener el Déficit federal de EE. utilice un rango más pequeño o más decimales.

 0
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-22 06:05:43

Creo que para una gran parte de sus requisitos o los de su cliente deben dictar qué precisión y escala usar. Por ejemplo, para el sitio web de comercio electrónico en el que estoy trabajando que se ocupa del dinero solo en libras esterlinas, se me ha pedido que lo mantenga al decimal( 6, 2 ).

 0
Author: ayaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-22 06:16:16

Una respuesta tardía aquí, pero he utilizado

DECIMAL(13,2)

Que estoy en lo cierto al pensar que debería permitir hasta 99,999,999,999.99.

 0
Author: Mike Upjohn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-24 12:54:16