web-dev-qa-db-fra.com

Devez-vous choisir les types de données MONEY ou DECIMAL (x, y) dans SQL Server?

Je suis curieux de savoir s’il existe ou non une réelle différence entre le type de données money et quelque chose comme decimal(19,4) (ce que l’argent utilise en interne, je crois).

Je sais que money est spécifique à SQL Server. Je veux savoir s’il existe une raison impérieuse de choisir l’une plutôt que l’autre; la plupart des exemples SQL Server (par exemple, la base de données AdventureWorks) utilisent money et non decimal pour des informations telles que les informations de prix. 

Devrais-je simplement continuer à utiliser le type de données money, ou l'utilisation de décimales présente-t-elle un avantage? Money est moins de caractères à taper, mais ce n'est pas une raison valable :)

383
Wayne Molina

Ne devriez-vous jamais utiliser d'argent? Ce n'est pas précis, et c'est de la pure ordure; toujours utiliser décimal/numérique.

Exécutez ceci pour voir ce que je veux dire:

DECLARE
    @mon1 MONEY,
    @mon2 MONEY,
    @mon3 MONEY,
    @mon4 MONEY,
    @num1 DECIMAL(19,4),
    @num2 DECIMAL(19,4),
    @num3 DECIMAL(19,4),
    @num4 DECIMAL(19,4)

    SELECT
    @mon1 = 100, @mon2 = 339, @mon3 = 10000,
    @num1 = 100, @num2 = 339, @num3 = 10000

    SET @mon4 = @mon1/@mon2*@mon3
    SET @num4 = @num1/@num2*@num3

    SELECT @mon4 AS moneyresult,
    @num4 AS numericresult

Sortie: 2949.0000 2949.8525

Pour certaines des personnes qui ont dit que vous ne divisez pas l'argent par l'argent:

Voici une de mes requêtes pour calculer les corrélations, et le changer en argent donne des résultats erronés.

select t1.index_id,t2.index_id,(avg(t1.monret*t2.monret)
    -(avg(t1.monret) * avg(t2.monret)))
            /((sqrt(avg(square(t1.monret)) - square(avg(t1.monret))))
            *(sqrt(avg(square(t2.monret)) - square(avg(t2.monret))))),
current_timestamp,@MaxDate
            from Table1 t1  join Table1 t2  on t1.Date = traDate
            group by t1.index_id,t2.index_id
280
SQLMenace

SQLMenace a déclaré que l'argent était inexact. Mais vous ne multipliez/divisez pas l'argent par l'argent! Combien coûtent 3 dollars x 50 cents? 150 Dollars? Vous multipliez/divisez l'argent par des scalaires, qui doivent être décimaux.

DECLARE
@mon1 MONEY,
@mon4 MONEY,
@num1 DECIMAL(19,4),
@num2 DECIMAL(19,4),
@num3 DECIMAL(19,4),
@num4 DECIMAL(19,4)

SELECT
@mon1 = 100,
@num1 = 100, @num2 = 339, @num3 = 10000

SET @mon4 = @mon1/@num2*@num3
SET @num4 = @num1/@num2*@num3

SELECT @mon4 AS moneyresult,
@num4 AS numericresult

Résultats dans le résultat correct:

moneyresult numericresult 
--------------------- ------------------------ ---------------
 2949.8525 2949.8525

money est bon tant que vous n'avez pas besoin de plus de 4 chiffres décimaux et que vous vous assurez que vos scalaires - qui ne représentent pas de l'argent - sont decimals.

253
configurator

Tout est dangereux si vous ne savez pas ce que vous faites

Même les types décimaux de haute précision ne peuvent pas sauver la journée:

declare @num1 numeric(38,22)
declare @num2 numeric(38,22)
set @num1 = .0000006
set @num2 = 1.0
select @num1 * @num2 * 1000000

1.000000 <- Doit être 0.6000000


Les types money sont des entiers

Les représentations textuelles de smallmoney et decimal(10,4) peuvent se ressembler, mais cela ne les rend pas interchangeables. Avez-vous un mouvement de recul lorsque vous voyez les dates enregistrées sous le nom varchar(10)? C'est la même chose.

Dans les coulisses, money/smallmoney n'est qu'un bigint/int Le point décimal de la représentation textuelle de money est un fluff visuel, tout comme les tirets dans une date aaaa-mm-jj. SQL ne les stocke pas réellement en interne.

En ce qui concerne decimal vs money, choisissez ce qui convient le mieux à vos besoins. Les types money existent, car il est très courant de stocker des valeurs comptables sous forme de multiples entiers de 1/10000ème d'unité. De plus, si vous traitez avec de l'argent réel et des calculs allant au-delà de simples additions et soustractions, vous ne devriez pas le faire au niveau de la base de données! Faites-le au niveau de l'application avec une bibliothèque qui prend en charge arrondi bancaire (IEEE 754)

57
Anon

Je me rends compte que WayneM a déclaré qu'il savait que l'argent est spécifique à SQL Server. Cependant, il demande s’il existe des raisons d’utiliser de l’argent au lieu de décimal ou inversement et j’estime qu’une raison évidente doit encore être invoquée: utiliser décimal signifie que c’est une chose de moins à craindre si vous devez changer votre SGBD - ce qui peut arriver.

Rendez vos systèmes aussi flexibles que possible!

39
Dean

Eh bien, j'aime bien MONEY! C'est un octet moins cher que DECIMAL, et les calculs sont plus rapides car (sous les couvertures), les opérations d'addition et de soustraction sont essentiellement des opérations entières. L'exemple de @ SQLMenace, qui est un excellent avertissement pour les ignorants, pourrait également s'appliquer à INTegers, où le résultat serait zéro. Mais ce n’est pas une raison pour ne pas utiliser d’entiers - le cas échéant .

Il est donc parfaitement "sûr" et approprié d’utiliser MONEY lorsque vous avez affaire à MONEY et de l’utiliser conformément aux règles mathématiques qu’il suit (identique à INTeger).

Aurait-il été préférable que SQL Server promeuve la division et la multiplication de MONEY en DECIMALs (ou FLOATs?) - éventuellement, mais ils n'ont pas choisi de le faire; ils n'ont pas non plus choisi de promouvoir INTegers en FLOATs lors de leur division.

MONEY n'a pas de problème de précision; que DECIMAL ait un type intermédiaire plus grand utilisé lors des calculs est simplement une "fonctionnalité" de l'utilisation de ce type (et je ne suis pas sûr de la portée de cette "fonctionnalité").

Pour répondre à la question spécifique, une "raison impérieuse"? Eh bien, si vous voulez des performances maximales absolues dans SUM(x)x pourrait être soit DECIMAL ou MONEY, alors MONEY aura un bord.

N'oubliez pas non plus que c'est un petit cousin, SMALLMONEY - il ne s'agit que de 4 octets, mais il ne dépasse pas 214,748.3647 - ce qui est assez petit pour de l'argent - et n'est donc pas souvent adapté.

Pour prouver l'intérêt d'utiliser des types intermédiaires plus volumineux, si vous affectez explicitement l'intermédiaire à une variable, DECIMAL souffre du même problème:

declare @a decimal(19,4)
declare @b decimal(19,4)
declare @c decimal(19,4)
declare @d decimal(19,4)

select @a = 100, @b = 339, @c = 10000

set @d = @a/@b

set @d = @d*@c

select @d

Produit 2950.0000 (d'accord, donc au moins DECIMAL arrondi plutôt que MONEY tronqué — comme un entier.)

30
dsz

En guise de contrepoint à l'orientation générale des autres réponses. Voir Les nombreux avantages de l'argent… Type de données! dans Guide SQLCAT du moteur relationnel

Plus précisément, je voudrais souligner les points suivants

En travaillant sur les implémentations client, nous avons trouvé des solutions intéressantes numéros de performance concernant le type de données de l'argent. Par exemple, quand Analysis Services a été défini sur le type de données monétaire (du double) à correspond au type de données d'argent SQL Server, il y avait une amélioration de 13% dans vitesse de traitement (lignes/sec). Pour obtenir des performances plus rapides dans SQL Server Integration Services (SSIS) pour charger 1,18 TB dans moins de trente ans minutes, comme indiqué dans SSIS 2008 - record mondial de performance ETL, c’était observé que la modification des quatre colonnes décimales (9,2) avec une taille de 5 octets dans la table LINEITEM de TPC-H en argent (8 octets) améliorés en bloc vitesse d’insertion de 20% ... L’amélioration des performances tient au protocole Tabular Data Stream (TDS) de SQL Server, qui a pour principe de conception de transférer des données sous forme binaire compacte et aussi proche que possible de la mémoire interne format de SQL Server. Empiriquement, cela a été observé lors du test de performance ETL record mondial de SSIS 2008 utilisant Kernrate; le protocole a chuté de manière significative lorsque le type de données est passé de décimal à argent Cela rend le transfert des données aussi efficace que possible. Un type de données complexe nécessite des cycles d'analyse et de traitement supplémentaires pour être traité. type à largeur fixe.

Donc, la réponse à la question est "ça dépend". Vous devez faire plus attention à certaines opérations arithmétiques afin de préserver la précision, mais vous constaterez peut-être que des considérations de performances le justifient.

12
Martin Smith

Nous venons tout juste de rencontrer un problème très similaire et je suis maintenant bien plus qu'un +1 pour ne jamais utiliser Money, sauf dans la présentation de niveau supérieur. Nous avons plusieurs tables (en réalité un bordereau de vente et une facture), chacune contenant un ou plusieurs champs Money pour des raisons historiques, et nous devons effectuer un calcul au prorata pour déterminer le pourcentage de la facture totale correspondant à la taxe. ligne sur le bon de vente. Notre calcul est 

vat proportion = total invoice vat x (voucher line value / total invoice value)

Il en résulte un calcul monétaire/monde réel qui provoque des erreurs d’échelle sur la partie division, qui se multiplient ensuite dans une proportion de cuve incorrecte. Lorsque ces valeurs sont ajoutées par la suite, nous obtenons une somme des proportions de la TVA qui ne correspond pas à la valeur totale de la facture. Si l'une des valeurs entre parenthèses avait été une décimale (je suis sur le point d'en utiliser une comme telle), la proportion de cuve serait correcte.

Lorsque les crochets n'existaient pas à l'origine, cela fonctionnait auparavant, je suppose qu'en raison des valeurs plus grandes impliquées, cela simulait effectivement une échelle plus élevée. Nous avons ajouté les crochets car il s'agissait tout d'abord de la multiplication, ce qui, dans de rares cas, a rendu plus précise la précision disponible pour le calcul, mais cette erreur est maintenant à l'origine de cette erreur beaucoup plus courante.

10
Kaideejee

Vous ne devriez pas utiliser d'argent lorsque vous devez multiplier/diviser la valeur. L'argent est stocké de la même manière qu'un entier, tandis que le point décimal est stocké sous forme de virgule décimale et de chiffres décimaux. Cela signifie que l'argent perdra de la précision dans la plupart des cas, tandis que décimal ne le fera que lorsqu'il sera reconverti à son échelle d'origine. L'argent est un point fixe, son échelle ne change donc pas pendant les calculs. Toutefois, comme il s'agit d'un point fixe lorsqu'il est imprimé sous forme de chaîne décimale (par opposition à une position fixe dans une chaîne de base 2), les valeurs allant jusqu'à l'échelle 4 sont exactement représentées. Donc, pour l'addition et la soustraction, l'argent va bien.

Une décimale est représentée en base 10 en interne et la position du point décimal est donc également basée sur le nombre en base 10. Ce qui fait que sa partie décimale représente sa valeur exactement, comme avec de l’argent. La différence est que les valeurs intermédiaires de la décimale peuvent conserver une précision allant jusqu'à 38 chiffres.

Avec un nombre à virgule flottante, la valeur est stockée en binaire comme s'il s'agissait d'un entier, et la position du point décimal (ou binaire, ahem) est relative aux bits représentant le nombre. Comme il s’agit d’un point décimal binaire, les nombres de base 10 perdent en précision juste après le point décimal. 1/5ème, ou 0,2, ne peut pas être représenté précisément de cette manière. Ni l'argent ni le nombre décimal ne souffrent de cette limitation.

Il est assez facile de convertir l’argent en décimal, d’effectuer les calculs, puis de stocker la valeur résultante dans un champ monétaire ou une variable.

À partir de mon point de vue, je veux que les choses qui arrivent aux chiffres se produisent sans avoir à trop y penser. Si tous les calculs doivent être convertis en nombres décimaux, alors pour moi, je ne souhaite utiliser que des nombres décimaux. J'économiserais le champ de l'argent à des fins d'affichage.

En ce qui concerne la taille, je ne vois pas assez de différence pour changer d’avis. L'argent prend 4 à 8 octets, alors que le nombre décimal peut être 5, 9, 13 et 17. Les 9 octets peuvent couvrir toute la plage que les 8 octets d'argent peuvent contenir. Indexage (la comparaison et la recherche doivent être comparables).

2
Gerard ONeill

J'ai trouvé une raison d'utiliser la décimale par rapport à l'argent en tant que sujet de précision.

DECLARE @dOne   DECIMAL(19,4),
        @dThree DECIMAL(19,4),
        @mOne   MONEY,
        @mThree MONEY,
        @fOne   FLOAT,
        @fThree FLOAT

 SELECT @dOne   = 1,
        @dThree = 3,    
        @mOne   = 1,
        @mThree = 3,    
        @fOne   = 1,
        @fThree = 3

 SELECT (@dOne/@dThree)*@dThree AS DecimalResult,
        (@mOne/@mThree)*@mThree AS MoneyResult,
        (@fOne/@fThree)*@fThree AS FloatResult

Testez-le et prenez votre décision.

1
QMaster

Je viens de voir cette entrée de blog: Money vs Decimal dans SQL Server.

Qui dit essentiellement que l'argent a un problème de précision ...

declare @m money
declare @d decimal(9,2)

set @m = 19.34
set @d = 19.34

select (@m/1000)*1000
select (@d/1000)*1000

Pour le type money, vous obtiendrez 19.30 au lieu de 19.34. Je ne suis pas sûr s'il existe un scénario d'application divisant l'argent en 1000 parties pour le calcul, mais cet exemple expose certaines limitations.

1
Harsha

Tous les articles précédents apportent des points valides, mais certains ne répondent pas précisément à la question.

La question qui se pose est la suivante: pourquoi quelqu'un préférerait-il l'argent alors que nous savons déjà qu'il s'agit d'un type de données moins précis et qu'il peut provoquer des erreurs s'il est utilisé dans des calculs complexes?

Vous utilisez de l'argent lorsque vous ne faites pas de calculs complexes et pouvez échanger cette précision contre d'autres besoins.

Par exemple, lorsque vous n'avez pas à effectuer les calculs ci-dessus ni à économiser 1 octet (l'argent prend 8 octets et la décimale (19,4) prend 9 octets).

Un autre exemple, lorsque vous n'avez pas à faire les calculs ci-dessus et que vous avez besoin de vitesse pour traiter une quantité énorme de données.

Un autre exemple, lorsque vous n'avez pas à effectuer les calculs ci-dessus et que vous devez importer des chaînes de texte de devise valides. Essayez de faire cela avec d'autres types numériques: SELECT CONVERT (MONEY, '$ 1,000,68')

0
Weber K.