Moneda con money, double o decimal en .NET y SQL Server
Una de las preguntas generales que se suelen hacer los desarrolladores de .NET y SQL Server es qué tipo de dato utilizar cuando queremos trabajar con monedas.
En SQL Server, además del tipo de datos Decimal, tenemos el tipo de datos Money y SmallMoney que son usados para los tipos de datos Currency o Moneda.
La diferencia entre ambos tipos es:
money 8 bytes -922,337,203,685,477.5808 a 922,337,203,685,477.5807 (-922,337,203,685,477.58 a 922,337,203,685,477.58)
smallmoney 4 bytes -214,748.3648 a 214,748.3647
Sin embargo, en SQL Server tenemos también, como decía antes, la posibilidad de usar el tipo de datos Decimal, como por ejemplo:
decimal(19,4)
Si queremos acceder a los formatos de tipo de moneda en el mundo de acuerdo a la información ISO 4217, te sugiero acceder al siguiente enlace:
De hecho, un comportamiento curioso en SQL Server con respecto a Money y Decimal, es el siguiente script:
DECLARE @money1 MONEY, @money2 MONEY, @money3 MONEY, @money4 MONEY, @decimal1 DECIMAL(19,4), @decimal2 DECIMAL(19,4), @decimal3 DECIMAL(19,4), @decimal4 DECIMAL(19,4) SELECT @money1 = 1, @money2 = 3, @money3 = 10000, @decimal1 = 1, @decimal2 = 3, @decimal3 = 10000 SET @money4 = (@money1/@money2) * @money3 SET @decimal4 = (@decimal1/@decimal2) * @decimal3 SELECT @money4 AS MoneyResult, @decimal4 AS DecimalResult
Este cálculo da los siguientes resultados:
- MoneryResult 3333,00
- DecimalResult 3333.3333
Así que como vemos, el uso de Money en lugar de Decimal en SQL Server podría devolvernos comportamientos no esperados inicialmente.
¿Debemos evitar el uso de Money en SQL Server?.
No, todo depende del contexto en el que nos encontramos y de cómo realicemos las operaciones con Money.
El script anterior, simplemente pone de relevancia que tenemos que tener cuidado cuando realizamos cálculos, sobre todo con tipos de dato Money.
Money de hecho, tiene sus deficiencias con respecto a la precisión, a las pruebas me remito.
Y tanto es así, que cambiando el orden de las operaciones del script anterior, el resultado es completamente diferente.
Por ejemplo, el siguiente script:
DECLARE @money1 MONEY, @money2 MONEY, @money3 MONEY, @money4 MONEY, @decimal1 DECIMAL(19,4), @decimal2 DECIMAL(19,4), @decimal3 DECIMAL(19,4), @decimal4 DECIMAL(19,4) SELECT @money1 = 1, @money2 = 3, @money3 = 10000, @decimal1 = 1, @decimal2 = 3, @decimal3 = 10000 SET @money4 = (@money1 * @money3) / @money2 SET @decimal4 = (@decimal1 * @decimal3) / @decimal2 SELECT @money4 AS MoneyResult, @decimal4 AS DecimalResult
Dará los siguientes resultados:
- MoneryResult 3333,3333
- DecimalResult 3333.3333
Sin embargo, con el tipo de datos decimal, como podemos ver, en ambos casos el resultado es el mismo.
¿Cómo se te queda el cuerpo?.
Dicho esto para que sirva como aviso a navegantes, centrémonos ahora en C#.
La duda sería si debemos usar decimal o double.
Veamos un sencillo ejemplo.
Dos cálculos exactamente iguales con un tipo decimal y otro double, y comparemos los resultados.
decimal decimalResult = 10 + 1.4m - 10; double doubleResult = 10 + 1.4f - 10;
Este cálculo devuelve los siguientes datos:
- decimalResult 1,4
- doubleResult 1,39999961853027
Si extraemos el resultado con por ejemplo:
xxx.ToString("C")
El resultado será 1,4 en ambos casos.
Otro ejemplo (algo forzado) es este otro, parecido al anterior:
int priceInt = 1 + (14 / 3); double priceDouble = 1 + (14f / 3); decimal priceDecimal = 1 + (14m / 3);
Los resultados aquí son:
- 5
- 5,66666650772095
- 5,6666666666666666666666666667
El tema a destacar aquí es que para almacenar y trabajar con monedas, se recomienda más el uso de decimal, ya que double puede introducir errores en el redondeo debido a la coma flotante, la cuál no puede representar todos los números con exactitud.
Trabajar erróneamente con monedas y hacer cálculos con ellas, es algo delicado ya que podría llevarnos a perder mucho dinero, sobre todo en los redondeos.
¡Happy Coding!
One Responseso far
Gracias Jorge por la información, muy importante, excelentemente explicación apoyada por ejemplo muy claros y detallados.