Domanda

Sto usando una colonna decimale per archiviare i valori di denaro in un database e oggi mi chiedevo quale precisione e scala usare.

Dato che le colonne di caratteri presumibilmente a larghezza fissa sono più efficienti, pensavo che lo stesso potesse essere vero per le colonne decimali. È vero?

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


Questo è quello che ho deciso di fare:

  • Memorizza i tassi di conversione (se applicabile) nella tabella delle transazioni stessa, come float
  • Memorizza la valuta nella tabella degli account
  • L'importo della transazione sarà un DECIMAL(19,4)
  • Tutti i calcoli che utilizzano un tasso di conversione saranno gestiti dalla mia applicazione, quindi mantengo il controllo dei problemi di arrotondamento

Non credo che un float per il tasso di conversione sia un problema, dato che è principalmente per riferimento, e lo proverò comunque con un decimale.

Grazie a tutti per il prezioso contributo.

È stato utile?

Soluzione

Se stai cercando una taglia unica, suggerirei DECIMAL (19, 4) è una scelta popolare (un veloce 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 è disponibile solo nello stile "versione 1.0" (ovvero non completamente implementato) in VB6 / VBA6 / Jet 4.0.

La regola empirica per archiviazione di valori decimali in virgola fissa è quella di memorizzare almeno una posizione decimale in più rispetto a quella effettivamente richiesta per consentire l'arrotondamento. Uno dei motivi per mappare il vecchio tipo Currency nel front-end a DECIMAL (19, 4) nel back-end era che Currency esibito arrotondamento dei banchieri per natura, mentre DECIMAL (p, s) arrotondato per troncamento.

Un ulteriore punto decimale in memoria per DECIMAL consente di implementare un algoritmo di arrotondamento personalizzato anziché prendere il valore predefinito del fornitore (e l'arrotondamento dei banchieri è, a dir poco, allarmante per un designer che si aspetta tutto valori che terminano in .5 per arrotondare da zero).

Sì, DECIMAL (24, 8) mi sembra eccessivo. La maggior parte delle valute sono quotate con quattro o cinque cifre decimali. Conosco situazioni in cui è richiesta una scala decimale di 8 (o più) ma è qui che è stato proporzionato un importo monetario "normale" (diciamo quattro cifre decimali), che implica la precisione decimale dovrebbe essere ridotto di conseguenza (in tali circostanze considerare anche un tipo a virgola mobile). E nessuno ha così tanti soldi al giorno d'oggi per richiedere una precisione decimale di 24 :)

Tuttavia, piuttosto che un approccio unico per tutti, alcune ricerche potrebbero essere in ordine. Chiedi al tuo progettista o esperto di dominio le regole di contabilità che potrebbero essere applicabili: GAAP, UE, ecc. Ricordo vagamente alcuni trasferimenti all'interno dello stato dell'UE con regole esplicite per l'arrotondamento a cinque decimali, quindi utilizzando DECIMAL (p, 6) per l'archiviazione. I contabili in genere sembrano favorire quattro decimali.


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


I progettisti Microsoft e del linguaggio hanno scelto l'arrotondamento del banchiere perché i progettisti hardware l'hanno scelto [citazione?]. È incluso negli standard dell'Institute of Electrical and Electronics Engineers (IEEE), per esempio. E i progettisti hardware l'hanno scelto perché i matematici lo preferiscono. Vedi Wikipedia ; parafrasando: l'edizione del 1906 di Probabilità e teoria degli errori chiamava questa "regola del computer" ("computer", ovvero esseri umani che eseguono calcoli).

Altri suggerimenti

Di recente abbiamo implementato un sistema che deve gestire i valori in più valute e convertirle tra loro, e ho capito alcune cose nel modo più difficile.

NON UTILIZZARE MAI NUMERI DI PUNTI GALLEGGIANTI PER SOLDI

L'aritmetica in virgola mobile introduce inesattezze che potrebbero non essere notate fino a quando non hanno rovinato qualcosa. Tutti i valori devono essere archiviati come numeri interi o decimali fissi e, se si sceglie di utilizzare un tipo decimale fisso, assicurarsi di comprendere esattamente cosa fa quel tipo sotto il cofano (ovvero, utilizza internamente un numero intero o virgola mobile tipo).

Quando è necessario eseguire calcoli o conversioni:

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

Quando converti un numero in virgola mobile in un numero intero nel passaggio 3, non limitarti a lanciarlo, usa una funzione matematica per arrotondarlo per primo. Questo di solito sarà round , anche se in casi speciali potrebbe essere floor o ceil . Conosci la differenza e scegli attentamente.

Memorizza il tipo di un numero accanto al valore

Questo potrebbe non essere così importante per te se stai gestendo solo una valuta, ma è stato importante per noi nella gestione di più valute. Abbiamo usato il codice di 3 caratteri per una valuta, come USD, GBP, JPY, EUR, ecc.

A seconda della situazione, può anche essere utile memorizzare:

  • Se il numero è prima o dopo le imposte (e quale era l'aliquota fiscale)
  • Se il numero è il risultato di una conversione (e da cosa è stato convertito)

Conosci i limiti di accuratezza dei numeri con cui hai a che fare

Per valori reali, vuoi essere preciso come la più piccola unità 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 più piccoli, nel qual caso si tratta di un diverso tipo di valore di valuta . Assicurati che il tuo codice sappia quale è quale e che non li confonda. Evita di usare valori in virgola mobile anche qui.


Aggiungendo tutte quelle regole insieme, abbiamo deciso le seguenti regole. Nel codice corrente, 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

Che 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 lo avessi chiarito prima, non usare float!

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

DECIMAL(13, 2)

Oppure, se è necessario rispettare GAAP , utilizzare:

DECIMAL(13, 4)

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

Anch'io preferisco DECIMAL a tipi di denaro specifici per DBMS, sei più sicuro di mantenere quel tipo di logica nell'applicazione IMO. Un altro approccio sulla stessa linea è semplicemente quello di utilizzare un [lungo] intero, con la formattazione in & # 164; unit.subunit per la leggibilità umana (& # 164; = simbolo di valuta) fatto a livello di applicazione.

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

Dalla documentazione online di SQL Server 2000:

I dati monetari rappresentano importi di denaro positivi o negativi. In Microsoft & # 174; SQL Server & # 8482; 2000, i dati monetari vengono archiviati utilizzando i tipi di dati money e smallmoney. I dati monetari possono essere memorizzati con una precisione di quattro cifre decimali. Utilizzare il tipo di dati di denaro per memorizzare valori nell'intervallo compreso tra -922.337.203.685.477.5808 e +922.337.203.685.477.5807 (richiede 8 byte per memorizzare un valore). Utilizzare il tipo di dati smallmoney per memorizzare valori nell'intervallo compreso tra -214.748.3648 e 214.748.3647 (richiede 4 byte per memorizzare un valore). Se è richiesto un numero maggiore di cifre decimali, utilizzare invece il tipo di dati decimali.

A volte dovrai 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 eseguirai qualsiasi tipo di operazione aritmetica nel DB (moltiplicando le tariffe di fatturazione e così via), probabilmente vorrai molta più precisione di quanto suggeriscano le persone qui, per gli stessi motivi per cui non avrei mai voluto usare niente di meno che un valore a virgola mobile a precisione doppia nel codice dell'applicazione.

Se si stesse utilizzando IBM Informix Dynamic Server, si avrebbe un tipo DENARO che è una variante minore sul tipo DECIMALE o NUMERICO. È sempre un tipo a virgola fissa (mentre DECIMAL può essere un tipo a virgola mobile). È possibile specificare una scala da 1 a 32 e una precisione da 0 a 32 (l'impostazione predefinita è una scala di 16 e una precisione di 2). Quindi, a seconda di ciò che è necessario conservare, è possibile utilizzare DECIMAL (16,2) - ancora abbastanza grande da contenere il deficit federale USA, al centesimo più vicino - oppure è possibile utilizzare un intervallo più piccolo o più decimali.

Penso che in gran parte i requisiti del cliente o del cliente debbano dettare quale precisione e scala utilizzare. Ad esempio, per il sito Web di e-commerce su cui sto lavorando che tratta denaro solo in GBP, mi è stato richiesto di mantenerlo in decimale (6, 2).

Una risposta tardiva qui, ma l'ho usata

DECIMAL(13,2)

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top