PostgreSQL: quel type de données doit être utilisé pour la devise?

128

On dirait que le Moneytype est déconseillé comme décrit ici

Mon application doit stocker des devises, quel type de données dois-je utiliser? Numérique, argent ou FLOTTEUR?

rêveur
la source
7
Si vous avez lu tout le fil, Numeric est la voie à suivre.
razpeitia
Pour toute personne travaillant avec plusieurs devises et soucieuse de stocker des codes de devise en plus des montants, vous pouvez consulter la modélisation des devises dans la base de données (SO) et ISO 4217 (Wikipedia). La réponse courte est que vous aurez besoin de deux colonnes.
Fabien Snauwaert
Ne pas utiliser d'argent
a_horse_with_no_name

Réponses:

90

Numérique avec précision forcée de 2 unités. N'utilisez jamais float ou float comme le type de données pour représenter la devise, car si vous le faites, les gens seront mécontents lorsque le résultat final du rapport financier est incorrect de + ou - quelques dollars.

Le type d'argent est simplement laissé pour des raisons historiques pour autant que je sache.

Chris Farmiloe
la source
9
Ce n'est pas pourquoi vous évitez la virgule flottante. Même Numeric aura des erreurs d'arrondi si vous divisez par tout ce qui ne se divise pas en une puissance de dix, quelle que soit la précision que vous utilisez. (La précision de 2 est de toute façon une mauvaise idée ... consultez la documentation.)
Doradus
6
Si vous voulez prendre en charge des devises arbitraires, ne faites jamais une échelle égale à 2 (en termes postgresql, la précision est un nombre de tous les chiffres, par exemple en 121.121, elle est égale à 6). Il existe des devises, telles que le dinar de Bahreïn, où 1000 sous-unités équivalent à une unité, et il y a des devises qui n'ont pas du tout de sous-unités.
Nikolay Arhipov
@NikolayArhipov bon point, donc le max est en faitscale - precision
Konrad
1
numeric(3,2)sera en mesure de stocker max9.99 3-2 = 1
Konrad
5
Ne faites pas cela! À moins que vous ne prévoyiez de multiplier par 100 avant de charger des valeurs dans d'autres langues, puis de faire des mathématiques avec des entiers, vous vous retrouverez avec des résultats erronés. Stockez les choses en centimes (la plus petite unité monétaire avec laquelle vous avez affaire) et évitez les tracas. Très mauvaise réponse dans de nombreux cas.
Avamander
111

Votre source n'est en aucun cas officielle. Il date de 2011 et je ne reconnais même pas les auteurs. Si le type de monnaie était officiellement "découragé", PostgreSQL le dirait dans le manuel - ce qu'il ne fait pas .

Pour une source plus officielle , lisez ce fil de discussion dans pgsql-general (à partir de cette semaine seulement!) , Avec les déclarations des principaux développeurs dont D'Arcy JM Cain (auteur original du type d'argent) et Tom Lane:

Réponse connexe (et commentaires!) Sur les améliorations des versions récentes:

Fondamentalement, moneya ses utilisations (très limitées). Le Wiki Postgres suggère de l'éviter en grande partie, sauf dans les cas étroitement définis. L'avantage numericest la performance .

decimalest juste un alias pour numericdans Postgres, et largement utilisé pour les données monétaires, étant de type "précision arbitraire". Le manuel :

Le type numericpeut stocker des nombres avec un très grand nombre de chiffres. Il est particulièrement recommandé pour stocker des montants monétaires et d'autres quantités où l'exactitude est requise.

Personnellement, j'aime stocker la monnaie comme integerreprésentant des cents si des cents fractionnaires ne se produisent jamais (essentiellement là où l'argent a du sens). C'est plus efficace que n'importe quelle autre des options mentionnées.

Erwin Brandstetter
la source
4
Il y a plusieurs discussions sur les listes de diffusion qui donnent l'impression que le type de monnaie n'est au moins pas recommandé, par exemple: ici: postgresql.nabble.com/Money-type-todos-td1964190.html#a1964192 plus pour être juste: le manuel pour la version 8.2 l' a appelé obsolète: postgresql.org/docs/8.2/static/datatype-money.html
a_horse_with_no_name
11
@a_horse_with_no_name: Votre lien est vers un fil de discussion datant de 2007, qui est également à l'époque où la 8.2 était la version actuelle et que le moneytype était, en fait, obsolète. Les problèmes ont été résolus et le type a été ajouté dans les versions ultérieures. Personnellement, j'aime stocker la monnaie comme integerreprésentant des cents.
Erwin Brandstetter
1
Erwin, vous avez peut-être raison de penser uniquement du point de vue d'une base de données. Cependant, si vous combinez Postgresql + Java, ce n'est PAS du tout bon (d'après mon expérience). En lisant votre commentaire, j'ai utilisé MONEY pour la plupart de mes champs de devises et maintenant j'obtiens cette exception Java: " SQLException s'est produite: org.postgresql.util.PSQLException: Mauvaise valeur pour le type double: 2,500.00 ". J'ai cherché sur Google et je n'ai trouvé aucune bonne solution, donc je suis dans la tâche ennuyeuse de les changer tous en NUMERIC ou DECIMAL maintenant !!!
MD
1
@MD: Désolé d'entendre ça, mais je n'ai évidemment pas parlé pour Java (ce que je ne peux pas). Le message d'erreur est étrange. "double"? Et le séparateur de milliers pourrait également être un problème. Vous voudrez peut-être commencer une nouvelle question à ce sujet.
Erwin Brandstetter
2
@PirateApp: Oui, mon préféré. Vous avez peut-être manqué la dernière phrase de ma réponse, en disant simplement cela.
Erwin Brandstetter
69

Vos choix sont:

  1. bigint: enregistrez le montant en centimes. C'est ce qu'utilisent les transactions EFTPOS.
  2. decimal(12,2): enregistrez le montant avec exactement deux décimales. C'est ce que la plupart des logiciels de grand livre utilisent.
  3. float: idée terrible - précision insuffisante. C'est ce qu'utilisent les développeurs naïfs.

L'option 2 est la plus courante et la plus simple à utiliser. Faites en sorte que la précision (12 dans mon exemple, ce qui signifie 12 chiffres en tout) soit grande ou petite, ce qui vous convient le mieux.

Notez que si vous agrégez plusieurs transactions résultant d'un calcul (par exemple impliquant un taux de change) en une seule valeur ayant une signification commerciale, la précision doit être plus élevée pour fournir une valeur macro précise; pensez à utiliser quelque chose comme decimal(18, 8)pour que la somme soit exacte et que les valeurs individuelles puissent être arrondies au centième de précision pour l'affichage.

Bohème
la source
10
Si vous travaillez avec n'importe quel type de calcul de taxe inversée ou de change, vous avez besoin d'au moins 4 décimales, sinon vous perdrez des données. Alors numeric(15,4)ou numeric(15,6)est une bonne idée.
Petrus Theron
3
Il existe une quatrième option: utiliser une chaîne et utiliser un type décimal équivalent sans perte dans la langue hôte.
ioquatix
2
qu'en est-il d'un entier mis à l'échelle, le stockage de 10000,045 ne ferait certainement pas de mal s'il était stocké sous la forme 10000045 avec un facteur de mise à l'échelle de 1000x?
PirateApp
26

Je garde tous mes champs monétaires comme:

numeric(15,6)

Il semble excessif d'avoir autant de décimales, mais s'il y a la moindre chance que vous ayez à gérer plusieurs devises, vous aurez besoin de beaucoup de précision pour la conversion. Peu importe ce que je présente à un utilisateur, je stocke toujours en dollars américains. De cette façon, je peux facilement convertir dans n'importe quelle autre devise, compte tenu du taux de conversion pour la journée concernée.

Si vous ne faites jamais rien d'autre qu'une devise, le pire ici est que vous avez perdu un peu d'espace pour stocker des zéros.

Michael Collette
la source
6
Cela présente un risque de résultats erronés en raison du manque de troncature. Si des valeurs différentes de zéro fuient involontairement dans les décimales restantes, par exemple, un champ de prix contenant 0,333333 dollars, alors vous pouvez avoir une situation où le système affiche le résultat d'une personne achetant 3 articles à 0,33 USD chacun, totalisant jusqu'à 1,00 USD au lieu de 0,99 USD.
Peteris
1
Perteris, alors que suggérez-vous à la place? Peu importe la précision que vous apportez à cet arrondi, cela peut être un problème. Je n'ai tout simplement pas trouvé de meilleur moyen, même si celui-ci n'est pas idéal.
Michael Collette
3
Point fixe et tronquer le cas échéant. Dès que vous atteignez une valeur monétaire «stockable», par exemple un prix offert à un client, il doit être dans des mesures appropriées, qui dans la plupart des cas seraient en centimes entiers dans un environnement de vente au détail standard. Si vous avez des besoins commerciaux différents (par exemple, le prix des produits à volume élevé par unité), il peut y avoir un paramètre de précision différent, mais vous devez traiter la présentation avec le stockage - si vous affichez le nombre d'argent avec x décimales (ou vice versa, par exemple en milliers) alors vous devez également le stocker avec cette précision, ni moins mais pas plus.
Peteris
Pour de nombreux sites liés au commerce de détail qui peuvent fonctionner. Le projet principal avec lequel je travaille peut avoir une partie ayant besoin de voir le même coût dans une devise, avec un client dans une autre devise, pour un fournisseur dans une troisième.
Michael Collette
19

Utilisez un entier 64 bits stocké sous bigint

Je recommande d'utiliser des micro-dollars (ou une devise similaire). Micro signifie 1 millionième donc 1 micro-dollar = 0,000001 $.

  • Simple à utiliser et compatible avec toutes les langues.
  • Assez de précision pour gérer des fractions de cent.
  • Fonctionne pour de très petits prix par unité (comme les impressions d'annonces ou les frais d'API).
  • Plus petite taille de données pour le stockage que les chaînes ou les valeurs numériques.
  • Précision facile à maintenir grâce à des calculs et application d'arrondi à la sortie finale
Mani Gandham
la source
1
C'est la réponse la plus correcte et tout logiciel raisonnable traite la plus petite unité monétaire en main. Faire tous les calculs sur des nombres entiers signifie que vous n'avez pas à gérer de déformation de flotteurs spécifique à une langue.
Avamander
1
J'utilisera ça aussi. Merci
Pourquoi est-ce trop numeric(15,6)suggéré dans une autre réponse?
Juliusz Gonera le
@JuliuszGonera Pour les raisons énumérées dans la réponse. Les entiers sont plus petits et supportés partout, et évitent tous les problèmes de troncature mathématique. Il utilise essentiellement des nombres mais en décalant les décimales pour obtenir un nombre entier beaucoup plus compatible.
Mani Gandham le
1
Ah, c'est vrai, j'ai raté la partie sur le stockage. Merci! En ce qui concerne "Simple à utiliser et compatible avec toutes les langues", JavaScript prend malheureusement en charge les entiers jusqu'à 9007199254740991, ce qui est plus de 1000 fois plus petit que la valeur maximale de bigint. Il existe developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... mais il est livré avec un support limité (pour l'instant) et des mises en garde (par exemple, vous ne pouvez pas le multiplier par un flottant facilement lors de la conversion de devises) . Étant donné que le maximum que vous pouvez stocker dans un entier JS en utilisant des micro-dollars est de 9 milliards de dollars, ce qui est probablement toujours bon dans la plupart des cas.
Juliusz Gonera le
4

Permet BigIntde stocker la devise sous la forme d'un entier positif représentant la valeur monétaire dans la plus petite unité monétaire (par exemple, 100 cents pour stocker 1,00 USD ou 100 cents pour stocker 100 ¥ (yen japonais, une devise à zéro décimal). C'est ce que fait Stripe - un les sociétés de services financiers les plus importantes pour le commerce électronique mondial.

Max Hodges
la source