Pregunta

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

Dado que supuestamente las columnas char de un ancho fijo son más eficientes, pensé que lo mismo podría ser cierto para las columnas decimales. ¿Lo es?

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


Esto es lo que he decidido hacer:

  • Almacene las tasas de conversión (cuando corresponda) en la propia tabla de transacciones, como un flotante
  • Almacena la moneda en la tabla de cuentas
  • El monto de la transacción será un DECIMAL(19,4)
  • Todos los cálculos que usen una tasa de conversión serán manejados por mi aplicación, así que mantengo el control de los problemas de redondeo

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

Gracias a todos por su valiosa aportación.

¿Fue útil?

Solución

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

La regla de oro para almacenamiento de valores decimales de punto fijo es almacenar al menos un lugar decimal más del que realmente necesita para permitir el redondeo. Una de las razones para asignar el antiguo tipo de Currency en el extremo frontal al tipo de DECIMAL (19, 4) en el back-end fue que Currency exhibió 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 todo valores que terminan en .5 para redondear fuera de cero).

Sí, DECIMAL (24, 8) suena como una exageración para mí. La mayoría de las monedas se cotizan con 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" (por ejemplo, cuatro lugares decimales), lo que implica la precisión decimal debe reducirse en consecuencia (también considere un tipo de punto 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 en dominios las reglas contables que pueden ser aplicables: GAAP, UE, etc. Recuerdo vagamente algunas transferencias dentro de los estados de la UE con reglas explícitas para redondear a cinco lugares decimales, por lo tanto, utilice DECIMAL (p, 6) para el almacenamiento. Los contadores generalmente parecen preferir cuatro decimales.


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


Microsoft y los diseñadores de idiomas eligieron el redondeo del banco porque los diseñadores de hardware lo eligieron [¿citación?]. 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. Consulte Wikipedia ; Para parafrasear: la edición de 1906 de Probabilidad y Teoría de los errores llamó a esto "la regla de la computadora" ("computadoras" significa seres humanos que realizan cálculos).

Otros consejos

Recientemente implementamos un sistema que necesita manejar valores en varias monedas y convertir entre ellas, y resolvimos algunas cosas de la manera más difícil.

NUNCA UTILICE NÚMEROS DE PUNTOS FLOTANTES PARA EL DINERO

La aritmética de punto flotante introduce inexactitudes que pueden no ser notadas hasta que hayan arruinado algo. Todos los valores deben almacenarse como tipos enteros o decimales fijos, y si elige usar un tipo decimal fijo, asegúrese de entender exactamente qué hace ese tipo debajo del capó (es decir, si usa internamente un número entero o punto flotante). tipo).

Cuando tenga que hacer cálculos o conversiones:

  1. Convertir valores a punto flotante
  2. Calcular nuevo valor
  3. Redondea el número y vuelve a convertirlo en un entero

Al convertir un número de coma flotante en un entero en el paso 3, no solo conviértalo, use una función matemática para redondearlo primero. Por lo general, esto será round , aunque en casos especiales podría ser floor o ceil . Conoce la diferencia y elige con cuidado.

Almacene el tipo de un número junto con el 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 fue la tasa impositiva)
  • Si el número es el resultado de una conversión (y de qué se convirtió)

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

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

Internamente, puede optar por tratar con valores más pequeños, en cuyo caso es un tipo diferente de valor de moneda . Asegúrese de que su código sepa cuál es cuál y qué no los confunda. Evita usar valores de punto flotante incluso aquí.


Agregando todas esas reglas juntas, decidimos las siguientes reglas. En el código en ejecución, las monedas se almacenan utilizando un número 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

Eso almacena el valor de $ 25.00. Solo pudimos hacerlo porque el código que trata con las monedas no tiene que estar dentro de la capa de la base de datos, por lo que todos los valores se pueden convertir primero en memoria. Sin duda, otras situaciones se prestarán a otras soluciones.


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

Cuando maneje dinero en MySQL, use DECIMAL (13,2) si conoce la precisión de sus valores monetarios o use DOBLE si solo quiere un valor aproximado lo suficientemente rápido. Entonces, si su aplicación necesita manejar valores monetarios de hasta un billón de dólares (o euros o libras), esto debería funcionar:

DECIMAL(13, 2)

O, si necesita cumplir con GAAP , use:

DECIMAL(13, 4)

4 decimales le darían la precisión para almacenar las subunidades de moneda más pequeñas del mundo. Puede reducirlo aún más si necesita la precisión del micropago (¿nanopago ?!).

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

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

De los Libros en pantalla de SQL Server 2000:

Los datos monetarios representan cantidades de dinero positivas o negativas. En Microsoft & # 174; SQL Server & # 8482; 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 lugares decimales. Utilice el tipo de datos de dinero 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 número mayor de lugares decimales, use el tipo de datos decimales en su lugar.

Algunas veces tendrá que ir a menos de un centavo y hay monedas internacionales que utilizan grandes demoniaciones. Por ejemplo, podría 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)

Si va a realizar algún tipo de operaciones aritméticas en la base de datos (multiplicando las tasas de facturación, etc.), probablemente querrá mucha más precisión de lo que sugiere la gente de aquí, por las mismas razones que nunca querría usar nada menos que un valor de punto flotante de doble precisión en el código de la aplicación.

Si estuviera usando IBM Informix Dynamic Server, tendría un tipo MONEY que es una variante menor en el tipo DECIMAL o NUMERIC. Siempre es un tipo de punto fijo (mientras que DECIMAL puede ser un tipo de punto flotante). Puede especificar una escala de 1 a 32, y una precisión de 0 a 32 (por defecto a una escala de 16 y una precisión de 2). Por lo tanto, dependiendo de lo que necesite almacenar, puede usar DECIMAL (16,2), que todavía es lo suficientemente grande para mantener el Déficit Federal de los EE. UU., Al centavo más cercano, o puede usar un rango más pequeño o más decimales.

Pienso que, en gran parte, los requisitos de usted o de su cliente deberían dictar qué precisión y escala utilizar. Por ejemplo, para el sitio web de comercio electrónico en el que estoy trabajando se trata de dinero solo en libras esterlinas, se me ha exigido que lo mantenga en Decimal (6, 2).

Una respuesta tardía aquí, pero la he usado

DECIMAL(13,2)

que tengo razón al pensar debería permitir hasta 99,999,999,999.99.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top