Memorizzazione di denaro in una colonna decimale: quale precisione e scala?

Sto usando una colonna decimale per memorizzare i valori monetari su un database, e oggi mi chiedevo quale precisione e scala usare.

Dal momento che le colonne presumibilmente char di larghezza fissa sono più efficienti, stavo pensando che lo stesso potrebbe essere vero per le colonne decimali. È?

E quale precisione e scala dovrei usare? Stavo pensando alla precisione 24/8. È eccessivo, non abbastanza o ok?


Questo è quello che ho deciso di fare:

  • Memorizza i tassi di conversione (quando applicabile) nella tabella delle transazioni stessa, come float
  • Memorizza la valuta nella tabella dell’account
  • L’importo della transazione sarà DECIMAL(19,4)
  • Tutti i calcoli che utilizzano un tasso di conversione verranno gestiti dalla mia applicazione in modo da mantenere il controllo dei problemi di arrotondamento

Non penso che un float per il tasso di conversione sia un problema, dal momento che è principalmente di riferimento, e lo cambierò comunque in un decimale.

Grazie a tutti per il vostro prezioso contributo.

Se stai cercando una taglia unica, suggerirei DECIMAL(19, 4) è una scelta popolare (un rapido Google lo conferma). Penso che questo provenga dal vecchio tipo di dati VBA / Access / Jet Currency, essendo il primo tipo decimale a virgola fissa nella lingua; Decimal solo in stile ‘versione 1.0’ (cioè non completamente implementato) in VB6 / VBA6 / Jet 4.0.

La regola empirica per la memorizzazione dei valori decimali a virgola fissa è quella di memorizzare almeno un decimale in più di quello effettivamente necessario per consentire l’arrotondamento. Uno dei motivi per la mapping del vecchio tipo di Currency nel front-end al tipo DECIMAL(19, 4) nel back-end era che la Currency esibiva l’arrotondamento dei banchieri per natura, mentre DECIMAL(p, s) arrotondato per troncamento.

Un decimale in più in memoria per DECIMAL consente di implementare un algoritmo di arrotondamento personalizzato piuttosto che l’impostazione predefinita del fornitore (e l’arrotondamento dei banchieri è allarmante, per non dire altro, per un progettista che prevede che tutti i valori terminano in 0,5 per arrotondare da zero ).

Sì, DECIMAL(24, 8) suona come eccessivo. La maggior parte delle valute è quotata a quattro o cinque cifre decimali. Conosco situazioni in cui è richiesta una scala decimale di 8 (o più), ma è qui che una quantità monetaria “normale” (diciamo quattro cifre decimali) è stata proporzionata, il che implica che la precisione decimale dovrebbe essere ridotta di conseguenza (considerare anche un tipo a virgola mobile in tali circostanze). E nessuno ha così tanti soldi al giorno d’oggi per richiedere una precisione decimale di 24 🙂

Tuttavia, piuttosto che un approccio “taglia unica”, alcune ricerche potrebbero essere in ordine. Chiedi al tuo designer o esperto di dominio sulle regole contabili che potrebbero essere applicabili: GAAP, UE, ecc. Ricordo vagamente alcuni trasferimenti intrasferici dell’UE con regole esplicite per arrotondare a cinque decimali, quindi utilizzando DECIMAL(p, 6) per l’archiviazione. I contabili generalmente sembrano preferire quattro cifre decimali.


PS Evita il tipo di dati MONEY SQL Server perché presenta problemi gravi con precisione durante l’arrotondamento, tra le altre considerazioni come la portabilità, ecc. Vedi il blog di Aaron Bertrand .


Microsoft e i progettisti di lingue hanno scelto l’arrotondamento del banchiere perché i progettisti dell’hardware lo hanno scelto [citazione?]. Ad esempio, è sancito dagli standard IEEE (Institute of Electrical and Electronics Engineers). E i progettisti dell’hardware lo hanno scelto perché i matematici lo preferiscono. Vedi Wikipedia ; per parafrasare: l’edizione del 1906 di Probabilità e teoria degli errori definiva questa “regola del computer” (“computer” che significa umani che eseguono calcoli).

Recentemente abbiamo implementato un sistema che ha bisogno di gestire i valori in più valute e di convertirli tra loro, e ha scoperto alcune cose nel modo più difficile.

NON UTILIZZARE MAI I NUMERI DEI PUNTI DI GALLEGGIO PER I SOLDI

L’aritmetica in virgola mobile introduce inesattezze che potrebbero non essere notate fino a quando non hanno avvitato qualcosa. Tutti i valori devono essere memorizzati come numeri interi o decimali fissi e, se si sceglie di utilizzare un tipo decimale fisso, assicurarsi di aver compreso esattamente cosa fa questo tipo di codice (cioè, utilizza internamente un numero intero o un punto mobile genere).

Quando hai bisogno di fare calcoli o conversioni:

  1. Converti i valori in virgola mobile
  2. Calcola nuovo valore
  3. Arrotonda il numero e riconvertilo in un numero intero

Quando converti un numero in virgola mobile in un numero intero al punto 3, non limitarti a lanciarlo: usa una funzione matematica per arrotondarlo per primo. Questo di solito sarà round , sebbene in casi speciali potrebbe essere un floor o un ceil . Conoscere la differenza e scegliere con attenzione.

Memorizza il tipo di un numero accanto al valore

Questo potrebbe non essere importante per te se gestisci solo una valuta, ma per noi è stato importante gestire più valute. Abbiamo utilizzato il codice a 3 caratteri per una valuta, come USD, GBP, JPY, EUR, ecc.

A seconda della situazione, potrebbe anche essere utile memorizzare:

  • Se il numero è prima o dopo le tasse (e quale era l’aliquota fiscale)
  • Se il numero è il risultato di una conversione (e da cosa è stata convertita)

Conoscere i limiti di precisione dei numeri con cui si ha a che fare

Per i valori reali, vuoi essere preciso quanto l’unità più piccola della valuta. Ciò significa che non hai valori inferiori a un centesimo, un centesimo, uno yen, un fen, ecc. Non memorizzare valori con una precisione superiore a quella senza motivo.

Internamente, puoi scegliere di gestire valori minori, nel qual caso si tratta di un diverso tipo di valore di valuta . Assicurati che il tuo codice sappia quali sono e quali non li confondono. Evita di usare valori in virgola mobile anche qui.


Aggiungendo tutte queste regole insieme, abbiamo deciso le seguenti regole. Nel codice in esecuzione, le valute vengono memorizzate utilizzando un numero intero per l’unità più piccola.

 class Currency { String code; // eg "USD" int value; // eg 2500 boolean converted; } class Price { Currency grossValue; Currency netValue; Tax taxRate; } 

Nel database, i valori sono memorizzati come una stringa nel seguente formato:

 USD:2500 

Ciò memorizza il valore di $ 25,00. Siamo stati in grado di farlo solo perché il codice che si occupa delle valute non ha bisogno di essere all’interno del livello del database stesso, quindi tutti i valori possono essere prima convertiti in memoria. Altre situazioni si presteranno senza dubbio ad altre soluzioni.


E nel caso non l’avessi chiarito prima, non usare float!

Quando gestisci denaro in MySQL, usa DECIMAL (13,2) se conosci la precisione dei tuoi valori monetari o usa DOUBLE se vuoi solo un valore approssimativo abbastanza buono. Quindi se la tua applicazione ha bisogno di gestire valori monetari fino a un trilione di dollari (o euro o sterline), allora questo dovrebbe funzionare:

 DECIMAL(13, 2) 

In alternativa, se è necessario rispettare GAAP, utilizzare:

 DECIMAL(13, 4) 

4 cifre decimali ti darebbero la precisione per memorizzare le sottounità valutarie più piccole del mondo. Puoi smetterla ulteriormente se hai bisogno di precisione micropagamento (nanopagamento ?!).

Anch’io preferisco i tipi di denaro DECIMAL specifici a DBMS, sei più sicuro nel mantenere questo tipo di logica nell’applicazione IMO. Un altro approccio lungo le stesse linee è semplicemente quello di usare un intero [lungo], con la formattazione in ¤unit.subunit per la leggibilità umana (¤ = simbolo di valuta) eseguita a livello di applicazione.

Il tipo di dati di denaro su SQL Server ha quattro cifre dopo il decimale.

Dalla documentazione in linea di SQL Server 2000:

I dati monetari rappresentano quantità di denaro positive o negative. In Microsoft® SQL Server ™ 2000, i dati monetari vengono archiviati utilizzando i tipi di dati money e smallmoney. I dati monetari possono essere archiviati con una precisione di quattro cifre decimali. Utilizzare il tipo di dati monetari per memorizzare valori nell’intervallo da -922,337,203,685,477,5808 a +922,337,203,685,477,5807 (sono necessari 8 byte per memorizzare un valore). Utilizzare il tipo di dati smallmoney per memorizzare i valori nell’intervallo da -214.748.3648 a 214.748.3647 (richiede 4 byte per memorizzare un valore). Se è richiesto un numero maggiore di posizioni decimali, utilizzare invece il tipo di dati decimali.

A volte devi andare a meno di un centesimo e ci sono valute internazionali che usano demoniazioni molto grandi. Ad esempio, potresti addebitare ai tuoi clienti 0,088 centesimi per transazione. Nel mio database Oracle le colonne sono definite come NUMBER (20,4)

Se hai intenzione di eseguire operazioni aritmetiche nel DB (moltiplicando i tassi di fatturazione e così via), probabilmente vorrai molta più precisione di quanto suggeriscono le persone qui, per le stesse ragioni per cui non avresti mai voglio usare qualcosa di meno di un valore in virgola mobile a doppia precisione nel codice dell’applicazione.

Se si stesse utilizzando IBM Informix Dynamic Server, si avrebbe un tipo MONEY che è una variante minore sul tipo DECIMAL o NUMERIC. È sempre un tipo a virgola fissa (mentre DECIMAL può essere un tipo a virgola mobile). È ansible specificare una scala da 1 a 32 e una precisione da 0 a 32 (per impostazione predefinita su una scala di 16 e una precisione di 2). Quindi, a seconda di cosa è necessario archiviare, potresti usare DECIMAL (16,2) – ancora abbastanza grande da contenere il disavanzo federale statunitense, al centesimo più vicino – o potresti usare un intervallo più piccolo o più cifre decimali.

Una risposta tardiva qui, ma l’ho usata

 DECIMAL(13,2) 

che ho ragione di pensare dovrebbe consentire fino a 99.999.999.999,99.

Penserei che per la maggior parte le esigenze del tuo o del tuo cliente dovrebbero dettare la precisione e la scala da usare. Ad esempio, per il sito di e-commerce su cui sto lavorando riguarda solo il denaro in GBP, mi è stato richiesto di tenerlo in Decimal (6, 2).