PostgreSQL: Welcher Datentyp sollte für die Währung verwendet werden?

128

Es scheint, als würde vom MoneyTyp abgeraten, wie hier beschrieben

In meiner Anwendung muss die Währung gespeichert werden. Welchen Datentyp soll ich verwenden? Numerisch, Geld oder FLOAT?

Tagträumer
quelle
7
Wenn Sie den gesamten Thread gelesen haben, ist Numerisch der richtige Weg.
Raspeitia
Wenn Sie mit mehreren Währungen arbeiten und zusätzlich zu den Beträgen Währungscodes speichern möchten, sollten Sie die Währungsmodellierung in Datenbank (SO) und ISO 4217 (Wikipedia) anzeigen . Die kurze Antwort lautet, dass Sie zwei Spalten benötigen.
Fabien Snauwaert
Verwenden Sie kein Geld
a_horse_with_no_name

Antworten:

90

Numerisch mit erzwungener Genauigkeit von 2 Einheiten. Verwenden Sie niemals float oder float like datatype, um die Währung darzustellen, denn wenn Sie dies tun, werden die Leute unglücklich sein, wenn das Endergebnis des Finanzberichts um + oder - ein paar Dollar falsch ist.

Die Geldart wird, soweit ich das beurteilen kann, nur aus historischen Gründen beibehalten.

Chris Farmiloe
quelle
9
Das ist nicht der Grund, warum Sie Gleitkomma vermeiden. Selbst Numeric weist Rundungsfehler auf, wenn Sie durch etwas dividieren, das sich nicht in eine Zehnerpotenz aufteilt, unabhängig von der verwendeten Präzision. (Präzision von 2 ist sowieso eine schlechte Idee ... überprüfen Sie die Dokumente.)
Doradus
6
Wenn Sie beliebige Währungen unterstützen möchten, machen Sie niemals eine Skala gleich 2 (in postgresql ausgedrückt ist die Genauigkeit eine Zahl aller Ziffern, z. B. in 121.121 ist sie gleich 6). Es gibt Währungen wie Bahrain Dinar, in denen 1000 Untereinheiten einer Einheit entsprechen, und es gibt Währungen, in denen es überhaupt keine Untereinheiten gibt.
Nikolay Arhipov
@ NikolayArhipov guter Punkt, also ist das Maximum tatsächlichscale - precision
Konrad
1
numeric(3,2)wird in der Lage sein, max9.99 3-2 = 1
Konrad
5
Mach das nicht! Wenn Sie nicht vorhaben, mit 100 zu multiplizieren, bevor Sie Werte in andere Sprachen laden und dann mit ganzen Zahlen rechnen, erhalten Sie falsche Ergebnisse. Speichern Sie Dinge in Cent (kleinste Währungseinheit, mit der Sie es zu tun haben) und sparen Sie sich Ärger. In vielen Fällen sehr schlechte Antwort.
Avamander
111

Ihre Quelle ist in keiner Weise offiziell. Es stammt aus dem Jahr 2011 und ich erkenne nicht einmal die Autoren. Wenn der Geldtyp offiziell "entmutigt" wäre, würde PostgreSQL dies im Handbuch sagen - was es nicht tut .

Für eine offiziellere Quelle lesen Sie diesen Thread in pgsql-general (ab dieser Woche!) Mit Aussagen von Kernentwicklern wie D'Arcy JM Cain (Originalautor des Geldtyps ) und Tom Lane:

Verwandte Antwort (und Kommentare!) Zu Verbesserungen in den letzten Versionen:

Grundsätzlich moneyhat seine (sehr begrenzte) Verwendung. Das Postgres-Wiki schlägt vor, es weitgehend zu vermeiden, mit Ausnahme der eng definierten Fälle. Der Vorteil gegenüber numericist die Leistung .

decimalist nur ein Alias ​​für numericin Postgres und wird häufig für Gelddaten verwendet, da es sich um einen Typ mit "willkürlicher Genauigkeit" handelt. Das Handbuch :

Der Typ numerickann Nummern mit einer sehr großen Anzahl von Ziffern speichern. Es wird insbesondere zur Aufbewahrung von Geldbeträgen und anderen Mengen empfohlen, bei denen Genauigkeit erforderlich ist.

Persönlich mag ich es, Währung als integerRepräsentation von Cent zu speichern, wenn gebrochene Cent niemals auftreten (im Grunde genommen, wo Geld Sinn macht). Das ist effizienter als jede andere der genannten Optionen.

Erwin Brandstetter
quelle
4
Es gibt mehrere Diskussionen auf den Mailinglisten, die den Eindruck erwecken, dass die Geldart zumindest nicht empfohlen wird, zB: hier: postgresql.nabble.com/Money-type-todos-td1964190.html#a1964192 plus um fair zu sein: die Handbuch für Version 8.2 hat es nennen veraltet: postgresql.org/docs/8.2/static/datatype-money.html
a_horse_with_no_name
11
@a_horse_with_no_name: Ihr Link führt zu einem Thread aus dem Jahr 2007, in dem auch 8.2 die aktuelle Version war und der moneyTyp tatsächlich veraltet war. Probleme wurden behoben und der Typ wurde in späteren Versionen wieder hinzugefügt. Persönlich mag ich es, Währung als integerRepräsentant von Cent zu speichern .
Erwin Brandstetter
1
Erwin, Sie können allein aus Datenbanksicht richtig denken. Wenn Sie jedoch Postgresql + Java kombinieren, ist dies (meiner Erfahrung nach) überhaupt nicht gut. Beim Lesen Ihres Kommentars habe ich für die meisten meiner Währungsfelder GELD verwendet. Jetzt wird die Java-Ausnahme angezeigt : " SQLException aufgetreten: org.postgresql.util.PSQLException: Ungültiger Wert für Typ double: 2.500,00 ". Ich habe gegoogelt und keine gute Lösung gefunden, also bin ich in der langweiligen Aufgabe, sie alle jetzt auf NUMERISCH oder DEZIMAL zu ändern !!!
MD
1
@MD: Tut mir leid das zu hören, aber ich habe offensichtlich nicht für Java gesprochen (was ich nicht kann). Die Fehlermeldung ist ungerade. "doppelt"? Und der Tausendertrenner könnte auch ein Problem sein. Vielleicht möchten Sie eine neue Frage dazu stellen.
Erwin Brandstetter
2
@PirateApp: Ja, mein persönlicher Favorit. Möglicherweise haben Sie den letzten Satz meiner Antwort verpasst und genau das gesagt.
Erwin Brandstetter
68

Sie haben folgende Möglichkeiten:

  1. bigint: Speichern Sie den Betrag in Cent. Dies ist, was EFTPOS-Transaktionen verwenden.
  2. decimal(12,2): Speichern Sie den Betrag mit genau zwei Dezimalstellen. Dies wird von den meisten Hauptbuchsoftware verwendet.
  3. float: schreckliche Idee - unzureichende Genauigkeit. Dies ist, was naive Entwickler verwenden.

Option 2 ist die häufigste und am einfachsten zu handhabende Option. Stellen Sie die Genauigkeit (12 in meinem Beispiel, dh insgesamt 12 Stellen) so groß oder klein ein, wie es für Sie am besten funktioniert.

Beachten Sie, dass die Genauigkeit höher sein sollte, um einen genauen Makrowert bereitzustellen, wenn Sie mehrere Transaktionen, die das Ergebnis einer Berechnung waren (z. B. mit einem Wechselkurs), zu einem einzigen Wert zusammenfassen, der geschäftliche Bedeutung hat. Verwenden Sie so etwas wie, decimal(18, 8)damit die Summe genau ist und die einzelnen Werte für die Anzeige auf Cent gerundet werden können.

Böhmisch
quelle
10
Wenn Sie mit einer Reverse-Tax-Berechnung oder einem Devisenhandel arbeiten, benötigen Sie mindestens 4 Dezimalstellen, da sonst Daten verloren gehen. Also numeric(15,4)oder numeric(15,6)ist eine gute Idee.
Petrus Theron
3
Es gibt eine vierte Option: Verwenden Sie einen String und einen entsprechenden verlustfreien Dezimaltyp in der Hostsprache.
Ioquatix
2
Was ist mit einer skalierten Ganzzahl? Das Speichern von 10000.045 würde sicherlich nicht schaden, wenn sie als 10000045 mit einem 1000-fachen Skalierungsfaktor gespeichert würde.
PirateApp
26

Ich behalte alle meine Geldfelder wie folgt:

numeric(15,6)

Es scheint übertrieben, so viele Dezimalstellen zu haben, aber wenn es auch nur die geringste Chance gibt, dass Sie mit mehreren Währungen umgehen müssen, benötigen Sie so viel Präzision für die Umrechnung. Egal, was ich einem Benutzer präsentiere, ich speichere immer in US-Dollar. Auf diese Weise kann ich angesichts des Umrechnungskurses für den jeweiligen Tag problemlos in jede andere Währung umrechnen.

Wenn Sie nie etwas anderes als eine Währung tun, ist das Schlimmste hier, dass Sie ein wenig Platz verschwendet haben, um einige Nullen zu speichern.

Michael Collette
quelle
6
Dies birgt das Risiko falscher Ergebnisse aufgrund fehlender Kürzungen. Wenn Werte ungleich Null ungewollt in die verbleibenden Dezimalstellen gelangen, z. B. in ein Preisfeld mit 0,333333 US-Dollar, kann es vorkommen, dass das System das Ergebnis anzeigt, dass jemand 3 Artikel zu je 0,33 US-Dollar gekauft hat, was insgesamt 1,00 US-Dollar statt 0,99 US-Dollar entspricht.
Peteris
1
Perteris, was schlagen Sie stattdessen vor? Egal wie viel Präzision Sie bei dieser Rundung einsetzen, kann ein Problem sein. Ich habe einfach keinen besseren Weg gefunden, auch wenn dieser nicht ideal ist.
Michael Collette
3
Fixpunkt und wo immer nötig abschneiden. Sobald Sie einen "speicherbaren" Geldwert erreichen, z. B. einen Preis, der einem Kunden angeboten wird, sollte dieser in geeigneten Metriken vorliegen, die in der Standard-Einzelhandelsumgebung in den meisten Fällen in ganzen Cent liegen würden. Wenn Sie unterschiedliche Geschäftsanforderungen haben (z. B. Preis für großvolumige Waren pro Einheit), gibt es möglicherweise eine andere Einstellung für die Genauigkeit. Sie müssen die Präsentation jedoch zusammen mit der Speicherung behandeln - wenn Sie die Geldnummer mit x Dezimalstellen anzeigen (oder umgekehrt). zB in ganzen Tausenden) dann müssen Sie es auch mit dieser Genauigkeit speichern , nicht weniger, aber auch nicht mehr.
Peteris
Für viele Websites im Zusammenhang mit dem Einzelhandel, die möglicherweise funktionieren. Bei dem Hauptprojekt, mit dem ich zusammenarbeite, muss möglicherweise eine Partei die gleichen Kosten in einer Währung sehen, mit einem Kunden in einer anderen Währung für einen Lieferanten in einer dritten.
Michael Collette
19

Verwenden Sie eine 64-Bit-Ganzzahl, die als gespeichert ist bigint

Ich empfehle die Verwendung von Mikrodollar (oder einer ähnlichen Hauptwährung). Mikro bedeutet 1 Millionstel, also 1 Mikrodollar = 0,000001 USD.

  • Einfach zu bedienen und mit jeder Sprache kompatibel.
  • Genug Präzision, um Bruchteile von einem Cent zu handhaben.
  • Funktioniert für sehr kleine Preise pro Einheit (wie Anzeigenimpressionen oder API-Gebühren).
  • Kleinere Datengröße für die Speicherung als Zeichenfolgen oder Zahlen.
  • Einfache Aufrechterhaltung der Genauigkeit durch Berechnungen und Anwenden von Rundungen am endgültigen Ausgang.
Mani Gandham
quelle
1
Dies ist die richtigste Antwort, und jede vernünftige Software befasst sich mit der kleinsten verfügbaren Währungseinheit. Wenn Sie alle Zahlen mit ganzen Zahlen rechnen, müssen Sie sich nicht mit sprachspezifischem Float-Mangling befassen.
Avamander
1
Wird das auch benutzen. Danke
Warum numeric(15,6)schlug dies in einer anderen Antwort vor?
Juliusz Gonera
@ JuliuszGonera Aus den in der Antwort aufgeführten Gründen. Ganzzahlen sind kleiner und werden überall unterstützt und vermeiden alle mathematischen Kürzungsprobleme. Grundsätzlich wird numerisch verwendet, aber die Dezimalstellen werden verschoben, sodass Sie eine ganze Zahl haben, die viel kompatibler ist.
Mani Gandham
1
Ah, richtig, ich habe den Teil über die Lagerung verpasst. Vielen Dank! In Bezug auf "Einfach zu bedienen und mit jeder Sprache kompatibel" unterstützt JavaScript leider Ganzzahlen bis zu 9007199254740991, was über 1000x kleiner als der Maximalwert von ist bigint. Es gibt developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…, aber es bietet (vorerst) nur eingeschränkte Unterstützung und Einschränkungen (z. B. können Sie es bei der Währungsumrechnung nicht einfach mit einem Float multiplizieren). . Angesichts der Tatsache, dass das Maximum, das Sie mit Mikrodollar in einer JS-Ganzzahl speichern können, 9 Milliarden US-Dollar beträgt, ist dies in den meisten Fällen wahrscheinlich immer noch gut.
Juliusz Gonera
3

Verwenden Sie BigIntdiese Option , um die Währung als positive Ganzzahl zu speichern, die den Geldwert in der kleinsten Währungseinheit darstellt (z. B. 100 Cent zum Speichern von 1,00 USD oder 100 zum Speichern von 100 Yen (japanischer Yen, eine Währung mit null Dezimalstellen). Dies ist, was Stripe tut - eine die wichtigsten Finanzdienstleistungsunternehmen für den globalen E-Commerce.

Max Hodges
quelle