PostgreSQL: quale tipo di dati dovrebbe essere usato per la valuta?


128

Sembra che il Moneytipo sia scoraggiato come descritto qui

La mia applicazione deve conservare la valuta, quale tipo di dati dovrei usare? Numerico, Denaro o FLOAT?


7
Se hai letto l'intero thread, Numereric è la strada da percorrere.
razpeitia,

Per chiunque lavori con più valute e si preoccupi di archiviare codici di valuta oltre agli importi, potresti voler vedere Modellazione di valuta nel database (SO) e ISO 4217 (Wikipedia). La risposta breve è che avrai bisogno di due colonne.
Fabien Snauwaert,

Risposte:


90

Numerico con precisione forzata di 2 unità. Non usare mai float o float come tipo di dati per rappresentare la valuta perché, in tal caso, le persone saranno infelici quando la cifra di fondo del rapporto finanziario non è corretta di + o - qualche dollaro.

Per quanto ne so, il tipo di denaro è rimasto solo per ragioni storiche.


9
Non è per questo che eviti il ​​virgola mobile. Anche Numerico avrà errori di arrotondamento se dividi per qualcosa che non si divide in una potenza di dieci, indipendentemente dalla precisione che usi. (La precisione di 2 è comunque una cattiva idea ... controlla i documenti.)
Doradus,

6
Se vuoi supportare valute arbitrarie, non fare mai una scala uguale a 2 (in termini postgresql, la precisione è un numero di tutte le cifre, ad esempio in 121.121 è uguale a 6). Ci sono valute, come il Bahrain Dinar, dove 1000 sottounità equivalgono a un'unità e ci sono valute che non hanno affatto sottounità.
Nikolay Arhipov,

@NikolayArhipov buon punto, quindi il massimo è in realtàscale - precision
Konrad

1
numeric(3,2)sarà in grado di memorizzare max9.99 3-2 = 1
Konrad

5
Non farlo! A meno che tu non preveda di moltiplicare per 100 prima di caricare qualsiasi valore in altre lingue e poi fare matematica con numeri interi, otterrai risultati errati. Conserva le cose in centesimi (la più piccola unità monetaria con cui hai a che fare) e risparmia qualche seccatura. Risposta molto negativa in molti casi.
Avamandro

111

La tua fonte non è in alcun modo ufficiale. Risale al 2011 e non riconosco nemmeno gli autori. Se il tipo di denaro fosse ufficialmente "scoraggiato", PostgreSQL lo direbbe nel manuale, cosa che non accade .

Per una fonte più ufficiale , leggi questa discussione in pgsql-general (solo da questa settimana!) , Con dichiarazioni di sviluppatori core tra cui D'Arcy JM Cain (autore originale del tipo di denaro) e Tom Lane:

Risposta correlata (e commenti!) Sui miglioramenti apportati alle versioni recenti:

Fondamentalmente, moneyha i suoi usi (molto limitati). Il Wiki di Postgres suggerisce di evitarlo ampiamente, ad eccezione di quei casi strettamente definiti. Il vantaggio numericè rappresentato dalle prestazioni .

decimalè solo un alias per numericPostgres e ampiamente utilizzato per i dati monetari, essendo un tipo di "precisione arbitraria". Il manuale :

Il tipo numericpuò memorizzare numeri con un numero molto elevato di cifre. È particolarmente raccomandato per la conservazione di importi monetari e altre quantità dove è richiesta la precisione.

Personalmente, mi piace conservare la valuta come integerrappresentante centesimi se i centesimi frazionari non si verificano mai (fondamentalmente dove il denaro ha un senso). È più efficiente di qualsiasi altra delle opzioni menzionate.


4
Ci sono diverse discussioni sulle mailing list che danno l'impressione che il tipo di denaro sia almeno sconsigliato, ad esempio: qui: postgresql.nabble.com/Money-type-todos-td1964190.html#a1964192 plus per essere onesti: il il manuale della versione 8.2 lo ha definito obsoleto: postgresql.org/docs/8.2/static/datatype-money.html
a_horse_with_no_name

11
@a_horse_with_no_name: il tuo link è a un thread del 2007, che è anche quando 8.2 era la versione corrente e il moneytipo era, in effetti, obsoleto. I problemi sono stati risolti e il tipo è stato aggiunto nuovamente nelle versioni successive. Personalmente mi piace conservare la valuta come integerrappresentante centesimi.
Erwin Brandstetter,

1
Erwin, potresti essere corretto pensando solo dal punto di vista del database. Tuttavia, se unisci Postgresql + Java, NON è affatto buono (dalla mia esperienza). Leggendo il tuo commento, ho usato SOLDI per la maggior parte dei miei campi di valuta e ora ottengo questa eccezione Java: "Si è verificata SQLException: org.postgresql.util.PSQLException: Valore errato per il tipo double: 2.500,00 ". Ho cercato su Google e non ho trovato una buona soluzione, quindi ora sono nel noioso compito di cambiarli tutti in NUMERIC o DECIMAL !!!
MD

1
@MD: Mi dispiace sentirlo, ma ovviamente non ho parlato per Java (cosa che non posso). Il messaggio di errore è dispari. "Doppio"? E anche il separatore delle migliaia potrebbe essere un problema. Potresti voler iniziare una nuova domanda a riguardo.
Erwin Brandstetter,

2
@PirateApp: Sì, il mio preferito personale. Potresti aver perso l'ultima frase della mia risposta, dicendo proprio questo.
Erwin Brandstetter,

68

Le tue scelte sono:

  1. bigint: memorizza l'importo in centesimi. Questo è ciò che usano le transazioni EFTPOS.
  2. decimal(12,2): memorizza l'importo con esattamente due cifre decimali. Questo è ciò che utilizza il software di contabilità generale.
  3. float: idea terribile - accuratezza inadeguata. Questo è ciò che usano gli sviluppatori ingenui.

L'opzione 2 è la più comune e la più semplice con cui lavorare. Rendi la precisione (12 nel mio esempio, che significa 12 cifre in tutto) grande o piccola come funziona meglio per te.

Si noti che se si stanno aggregando più transazioni risultanti da un calcolo (ad es. Comportando un tasso di cambio) in un singolo valore con significato commerciale, la precisione dovrebbe essere maggiore per fornire un valore macro accurato; considerare l'utilizzo di qualcosa del genere in decimal(18, 8)modo che la somma sia accurata e che i singoli valori possano essere arrotondati con precisione al centesimo per la visualizzazione.


10
Se si lavora con qualsiasi tipo di calcolo delle imposte inverse o di valuta estera, sono necessarie almeno 4 cifre decimali o si perderanno i dati. Quindi numeric(15,4)o numeric(15,6)è una buona idea.
Petrus Theron,

3
Esiste una quarta opzione, ovvero utilizzare una stringa e un tipo decimale equivalente non lossy nella lingua host.
ioquatix,

2
che dire di un numero intero ridimensionato, la memorizzazione di 10000.045 non danneggerebbe se fosse memorizzata come 10000045 con un fattore di ridimensionamento 1000x?
PirateApp

26

Tengo tutti i miei campi monetari come:

numeric(15,6)

Sembra eccessivo avere così tanti decimali, ma se c'è anche la minima possibilità che tu abbia a che fare con più valute avrai bisogno di molta precisione per la conversione. Indipendentemente da ciò che sto presentando a un utente, conservo sempre in dollari USA. In questo modo posso facilmente convertire in qualsiasi altra valuta, dato il tasso di conversione per il giorno in questione.

Se non fai mai altro che una valuta, la cosa peggiore qui è che hai perso un po 'di spazio per memorizzare alcuni zero.


6
Ciò comporta il rischio di risultati errati a causa della mancanza di troncamento. Se i valori diversi da zero si verificano involontariamente nelle posizioni decimali rimanenti, ad esempio un campo di prezzo contenente 0,333333 dollari, è possibile che si verifichi una situazione in cui il sistema mostra il risultato di qualcuno che acquista 3 articoli a $ 0,33 ciascuno, per un totale di $ 1,00 anziché $ 0,99.
Peteris,

1
Perteris, cosa suggerisci invece? Non importa quanta precisione passi a questo arrotondamento può essere un problema. Semplicemente non ho trovato un modo migliore, anche se questo non è l'ideale.
Michael Collette,

3
Punto fisso e troncare dove appropriato. Non appena si raggiunge un valore monetario "memorizzabile", ad esempio un prezzo offerto a un cliente, dovrebbe essere in metriche appropriate, che nella maggior parte dei casi si troverebbero in centesimi nell'ambiente di vendita standard. Se hai esigenze aziendali diverse (ad es. Prezzo di merci ad alto volume per unità), potrebbe esserci un'impostazione di precisione diversa, ma devi trattare la presentazione insieme all'archiviazione - se visualizzi il numero di denaro con x decimali (o viceversa, ad es. in migliaia intere) quindi è necessario memorizzarlo con tale precisione, non meno ma anche non di più.
Peteris,

Per molti siti correlati al dettaglio che potrebbero funzionare. Il progetto principale con cui lavoro potrebbe avere una parte che deve vedere lo stesso costo in una valuta, con un cliente in un'altra valuta, per un fornitore in una terza posizione.
Michael Collette,

19

Utilizzare un numero intero a 64 bit memorizzato come bigint

Raccomando di usare micro-dollari (o simile valuta principale). Micro significa 1 milionesimo quindi 1 micro-dollaro = $ 0,000001.

  • Semplice da usare e compatibile con ogni lingua.
  • Abbastanza precisione per gestire le frazioni di un centesimo.
  • Funziona con prezzi per unità molto piccoli (come impressioni dell'annuncio o addebiti API).
  • Dimensioni dei dati inferiori per l'archiviazione rispetto a stringhe o numeri.
  • Precisione di facile manutenzione tramite calcoli e applicazione dell'arrotondamento all'output finale.

1
Questa è la risposta più corretta e qualsiasi software ragionevole si occupa dell'unità valutaria più piccola in mano. Fare tutti i calcoli sugli interi significa che non devi avere a che fare con una manomissione galleggiante specifica per la lingua.
Avamandro

1
Lo userò anche io. Grazie

Perché questo numeric(15,6)suggerito in un'altra risposta?
Juliusz Gonera,

@JuliuszGonera Per i motivi elencati nella risposta. I numeri interi sono più piccoli e supportati ovunque ed evitano tutti i problemi di troncamento matematico. In pratica utilizza numerico ma sposta i decimali in modo da avere un numero intero molto più compatibile.
Mani Gandham,

1
Ah, giusto, mi sono perso la parte relativa all'archiviazione. Grazie! Per quanto riguarda "Semplice da usare e compatibile con ogni lingua", sfortunatamente JavaScript supporta numeri interi fino a 9007199254740991, che è oltre 1000x più piccolo del valore massimo di bigint. C'è developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ma viene fornito con supporto limitato (per ora) e avvertenze (ad esempio non è possibile moltiplicarlo facilmente per un float durante la conversione di valuta) . Dato che il massimo che puoi memorizzare in un numero intero JS usando micro-dollari è $ 9 miliardi che probabilmente è ancora buono per la maggior parte dei casi.
Juliusz Gonera,

3

Utilizzare BigIntper memorizzare la valuta come numero intero positivo che rappresenta il valore monetario nell'unità di valuta più piccola (ad esempio, 100 centesimi per memorizzare $ 1,00 o 100 per memorizzare 100 ¥ (yen giapponese, una valuta decimale zero). Questo è ciò che fa Stripe - uno le più importanti società di servizi finanziari per l'e-commerce globale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.