El mejor tipo de datos para almacenar valores monetarios en MySQL

279

Quiero almacenar muchos registros en una base de datos MySQL. Todos ellos contienen valores monetarios. Pero no sé cuántos dígitos se insertarán para cada uno.
¿Qué tipo de datos debo usar para este propósito?
VARCHAR o INT (u otros tipos de datos numéricos)?

Mohammad Saberi
fuente
13
deimal(10,2)es lo que uso ... puedes ajustar los valores dependiendo del tamaño esperado
Manse
9
La pregunta relacionada es Mejor tipo de datos para moneda ;).
shA.t

Respuestas:

370

Dado que el dinero necesita una representación exacta, no use tipos de datos que sean solo aproximados float. Puede usar un tipo de datos numéricos de punto fijo para ese tipo de

decimal(15,2)
  • 15 es la precisión (longitud total del valor, incluidos los decimales)
  • 2 es el número de dígitos después del punto decimal

Ver tipos numéricos de MySQL :

Estos tipos se utilizan cuando es importante preservar la precisión exacta, por ejemplo, con datos monetarios .

juergen d
fuente
3
¿Cuál podría ser la diferencia entre el tipo de datos decimal y numérico para este caso?
Emilio Gort
6060
En MySQL decimaly numericson lo mismo.
juergen d
21
Personalmente uso numeric(19,4)para registros financieros que te dan una mejor mano para jugar y adoptar nuevas solicitudes fácilmente.
YahyaE
10
Estoy de acuerdo con YahyaE, más decimales es mejor. Hay algunas monedas que generalmente usan 3 decimales, como los dinares de Bahréin, jordano o kuwaití, por lo que necesita al menos 3. Cuatro o cinco son mejores.
Edwin Hoogerbeets
1
@EdwinHoogerbeets No siendo contable ... pero dirigiendo un pequeño negocio en el Reino Unido ... Recuerdo haber leído en algún lugar hace mucho tiempo que las cifras de divisas deberían almacenarse en 4 decimales incluso por £, $, etc. para que ciertos cálculos puedan en realidad usa los últimos 2 decimales para ciertos contextos contables oscuros. Necesitamos un contador para confirmar / refutar.
Mike roedor
88

Puedes usar DECIMALo NUMERICambos son iguales

Los tipos DECIMAL y NUMERIC almacenan valores de datos numéricos exactos. Estos tipos se utilizan cuando es importante preservar la precisión exacta, por ejemplo, con datos monetarios. En MySQL, NUMERIC se implementa como DECIMAL, por lo que las siguientes observaciones sobre DECIMAL se aplican igualmente a NUMERIC. : MySQL

es decir DECIMAL(10,2)

Configuraciones de ejemplo

Buena lectura

NullPoiиteя
fuente
3
Puede ser confuso, pero su captura de pantalla no coincide con el texto de respuesta (precisión, escala).
Patrick Hofman
Estoy usando decimal (10,2) para mi valor monetario, sin embargo, cuando pongo algo como 867,000.00 se guarda como 867. ¿Qué estoy haciendo mal?
codeinprogress
32

Prefiero usar BIGINTy almacenar los valores multiplicando por 100 , para que se convierta en entero.

Por ejemplo, para representar un valor de moneda de 93.49, el valor se almacenará como 9349, mientras se muestra el valor que podemos dividir por 100 y mostrar. Esto ocupará menos espacio de almacenamiento.

Precaución: la
mayoría de las veces no realizamos la currency * currencymultiplicación, en caso de que lo hagamos, dividimos el resultado entre 100 y almacenamos, para que vuelva a la precisión adecuada.

Dinesh PR
fuente
Recuerdo que un profesor me dijo algo similar en mi curso universitario de Computer Systems. Me enseñaron que la forma más precisa es almacenar en centavos (o centavos) al multiplicar por 100 y guardar como un entero y dividir por 100 para mostrarlo al usuario. Supongo que esto tiene beneficios en términos de precisión y rendimiento del sistema de base de datos.
LondonAppDev
11
¿Cuál es la ventaja sobre DECIMAL? Creas la necesidad de traducir centavos a dólares, y ay si lo olvidas en algún momento.
1
El espacio es la única ventaja, pero sí, debemos ser más cuidadosos cuando usamos esta función.
Dinesh PR
44
En caso de que no sea obvio: tenga cuidado al usar el método de eliminación de escala si almacena dinero en centavos fraccionales (por ejemplo, $0.005o $0.12345) porque no se reducirán a un número entero después de multiplicar por 100. Si conoce la precisión de los valores, está claro que La mejor opción es usar DECIMAL. Pero si no conoce la precisión (como en mis ejemplos) entonces ... ¿ FLOATsería apropiado?
Quinn Commandado el
1
Una ventaja de este método viene cuando se usa un lenguaje como JavaScript que usa IEEE-754 para almacenar números de punto flotante. Esta especificación no garantiza que 0.1 + 0.2 === 0.3 sea verdadero. El almacenamiento de moneda como un número entero garantiza que su aplicación no incurrirá en ese tipo de error. Sin embargo, esta puede no ser la mejor solución. Llegué a esta página mientras investigaba soluciones y aún no he terminado.
Gary Ott
27

Depende de tu necesidad.

Por lo DECIMAL(10,2)general, usar es suficiente, pero si necesita valores un poco más precisos, puede establecerlos DECIMAL(10,4).

Si trabaja con grandes valores, reemplácelos 10por 19.

Svetoslav
fuente
Estoy usando decimal (10,2) para mi valor monetario, sin embargo, cuando pongo algo como 867,000.00 se guarda como 867. ¿Qué estoy haciendo mal?
codeinprogress
2
@codeinprogress ¿Usa el localizador / separador decimal incorrecto?
David Balažic
15

Si su aplicación necesita manejar valores monetarios de hasta un billón, esto debería funcionar: 13,2 Si necesita cumplir con los GAAP (Principios de contabilidad generalmente aceptados), use: 13,4

Por lo general, debe sumar sus valores monetarios en 13,4 antes de redondear la salida a 13,2.

david.ee
fuente
55
Si va a tomar Bitcoin, necesitará 8 decimales, aunque la mayoría de las billeteras van a mBTC, que es 3 en.wikipedia.org/wiki/Bitcoin
Christian
No pienses que esta respuesta es verdadera. opendata.stackexchange.com/a/10348/13983 @ david.e ¿tiene una fuente para eso?
Evan Carroll
@EvanCarroll déjame responder por david.ee. Creo que este artículo puede ser la fuente rietta.com/blog/2012/03/03/best-data-types-for-currencymoney-in
naXa
@naXa el enlace no cita nada de ninguna fuente que respalde la afirmación de usar 13,4 para GAAP. Todo lo que hizo fue vincular a un artículo que hace el mismo reclamo sin fundamento.
iheanyi
6

De hecho, esto se basa en las preferencias del programador. Yo personalmente uso: numeric(15,4)para cumplir con los Principios de contabilidad generalmente aceptados ( GAAP ) .

Chagbert
fuente
55
No tiene nada que ver con las "preferencias del programador" o con lo que "usa personalmente". Está dictado por el dominio del problema, que requiere una raíz decimal. Este no es un asunto en el que el programador pueda ejercer sus propias preferencias personales.
Marqués de Lorne
3

Intenta usar

Decimal(19,4)

esto generalmente funciona con cualquier otro DB también

Deepesh
fuente
3

Nosotros usamos double.

*jadear*

¿Por qué?

Porque puede representar cualquier número de 15 dígitos sin restricciones sobre dónde está el punto decimal . ¡Todo por unos miserables 8 bytes!

Entonces puede representar:

  • 0.123456789012345
  • 123456789012345.0

... y cualquier cosa en el medio.

Esto es útil porque estamos tratando con monedas globales , ydouble podemos almacenar los diversos números de decimales que probablemente encontraremos.

Un solo doublecampo puede representar 999,999,999,999,999s en yenes japoneses, 9,999,999,999,999.99s en dólares estadounidenses e incluso 9,999,999.999999999s en bitcoins

Si intenta hacer lo mismo con decimal, necesita lo decimal(30, 15)que cuesta 14 bytes.

Advertencias

Por supuesto, el uso doubleno está exento de advertencias.

Sin embargo, no es una pérdida de precisión como algunos tienden a señalar. Aunque doublepuede no ser internamente exacto al sistema base 10 , podemos hacerlo exacto redondeando el valor que extraemos de la base de datos a sus decimales significativos. Si es necesario, eso es. (por ejemplo, si se va a generar y se requiere una representación de base 10).

Las advertencias son que, cada vez que realizamos operaciones aritméticas con él, necesitamos normalizar el resultado (redondeándolo a sus decimales significativos) antes de:

  1. Realizando comparaciones en él.
  2. Escribiéndolo nuevamente en la base de datos.

Otro tipo de advertencia es que, a diferencia de decimal(m, d)donde la base de datos evitará que los programas inserten un número con más de mdígitos, no existen tales validaciones double. Un programa podría insertar un valor ingresado por el usuario de 20 dígitos y terminará siendo registrado silenciosamente como una cantidad inexacta.

antak
fuente
La primera vez que veo una respuesta como esta, interesante. P: Si escribo un flotante como 1.41 en la base de datos y, por alguna razón, necesito multiplicarlo por un número enorme en mysql, como 1,000,000,000,000. ¿El resultado redondeado será exactamente: 1.410.000.000.000?
roelleor
@roelleor Para que el resultado sea exactamente 1,410,000,000,000 (coma como separador de miles), se supone que la entrada es 1.410000000000(doce lugares decimales significativos), pero multiplicar eso por 1,000,000,000,000 (que son 13 dígitos significativos a la izquierda del punto decimal) significa que estamos trabajando en menos un total combinado de 25 dígitos de importancia. Esto supera con creces los 15 disponibles para un doble, por lo que, en cuanto al diseño, creo que estaría muy roto.
antak
2

En el momento en que se hizo esta pregunta, nadie pensó en el precio de Bitcoin. En el caso de BTC, probablemente sea insuficiente para usar DECIMAL(15,2). Si el Bitcoin aumentará a $ 100,000 o más, necesitaremos al menos DECIMAL(18,9)admitir criptomonedas en nuestras aplicaciones.

DECIMAL(18,9)ocupa 12 bytes de espacio en MySQL ( 4 bytes por 9 dígitos ).

bizwiz
fuente
> Un bitcoin se puede dividir a 8 decimales. Por lo tanto, 0.00000001 BTC es la cantidad más pequeña que se puede manejar en una transacción. Creo que te refieres a 8 en lugar de 9?
peligro89
1
Lo sé, pero 9 ocupa el mismo espacio en disco que 8. De los documentos de MySQL: "Los valores para las columnas DECIMAL se almacenan usando un formato binario que contiene nueve dígitos decimales en 4 bytes"
bizwiz
Ow lo siento, ahora te entiendo. Gracias.
peligro89
2

Almacenar dinero BIGINTmultiplicado por 100 o más con la razón de usar menos espacio de almacenamiento no tiene sentido en todas las situaciones "normales".

  • Para mantenerse alineado con GAAP es suficiente almacenar monedas en DECIMAL(13,4)
  • El manual de MySQL dice que necesita 4 bytes por cada 9 dígitos para almacenar DECIMAL.
  • DECIMAL(13,4) representa 9 dígitos + 4 dígitos de fracción (lugares decimales) => 4 + 2 bytes = 6 bytes
  • comparar a 8 bytes necesarios para almacenar BIGINT.
digitalstraw
fuente