PostgreSQL: Para Birimi için hangi Veri türü kullanılmalıdır?


129

BuradaMoney açıklandığı gibi tip önerilmiyor gibi görünüyor

Uygulamamın para birimini saklaması gerekiyor, hangi veri türünü kullanmalıyım? Sayısal, Para veya FLOAT?


7
Tüm diziyi okuduysanız, gidilecek yol Numeric'dir.
razpeitia

Birden fazla para birimiyle çalışan ve tutarlara ek olarak para birimi kodlarını saklamayı önemseyen herkes için, veritabanında (SO) ve ISO 4217'de (Wikipedia) Para Birimi modellemesini görmek isteyebilirsiniz . Kısa cevap, iki sütuna ihtiyacınız olacak.
Fabien Snauwaert

Yanıtlar:


91

Zorlanmış 2 birim hassasiyetli sayısal . Para birimini temsil etmek için asla float veya float benzeri veri türü kullanmayın, çünkü bunu yaparsanız, finansal raporun alt satır rakamı + veya - birkaç dolar yanlış olduğunda insanlar mutsuz olacaklar.

Para türü, söyleyebileceğim kadarıyla tarihsel nedenlerden dolayı bırakıldı.


9
Bu yüzden kayan noktadan kaçınmıyorsunuz. Hangi hassasiyeti kullanırsanız kullanın, üssüne bölünmeyen herhangi bir şeye bölerseniz Numeric bile yuvarlama hatalarına sahip olacaktır. (2'nin kesinliği zaten Kötü bir Fikirdir ... belgelere bakın.)
Doradus

6
Rasgele para birimlerini desteklemek istiyorsanız, asla 2'ye eşit bir ölçek yapmayın (postgresql terimlerinde, hassasiyet tüm basamakların sayısıdır, örneğin 121.121'de 6'ya eşittir). Bahreyn Dinarı gibi 1000 alt birimin bir birime eşit olduğu para birimleri ve hiç alt birimi olmayan para birimleri vardır.
Nikolay Arhipov

@NikolayArhipov iyi bir nokta, yani maksimum aslındascale - precision
Konrad

1
numeric(3,2)en fazla saklayabilecek9.99 3-2 = 1
Konrad

5
Bunu yapma! Diğer dillere herhangi bir değer yüklemeden önce 100 ile çarpmayı ve ardından tamsayılarla matematik yapmayı planlamıyorsanız, yanlış sonuçlarla karşılaşacaksınız. Eşyaları sent olarak (uğraştığınız en küçük para birimi) saklayın ve kendinizi biraz güçlükten kurtarın. Çoğu durumda çok kötü cevap.
Avamander

112

Kaynağınız hiçbir şekilde resmi değil. 2011 yılına dayanıyor ve yazarları bile tanımıyorum. Para türü resmi olarak "cesareti kırılmış" olsaydı, PostgreSQL bunu kılavuzda söylerdi - ki öyle değildir .

Daha resmi bir kaynak için , D'Arcy JM Cain (para türünün orijinal yazarı) ve Tom Lane gibi çekirdek geliştiricilerin ifadelerini içeren bu konuyu pgsql-general'de (sadece bu haftadan itibaren!) Okuyun:

Son sürümlerdeki iyileştirmelerle ilgili yanıtlar (ve yorumlar!):

Temel olarak, money(çok sınırlı) kullanımları vardır. Postgres Wiki büyük ölçüde, bunu önlemek olanlar dar tanımlanmış durumlar hariç öneriyor. Avantajı fazla numericolan performans .

decimalsadece numericPostgres için bir takma addır ve parasal veriler için yaygın olarak kullanılır, "keyfi bir hassasiyet" türüdür. Kılavuz :

Tür numeric, çok sayıda basamaklı sayıları saklayabilir. Özellikle parasal miktarların ve kesinliğin gerekli olduğu diğer miktarların depolanması için tavsiye edilir.

Şahsen, integereğer kesirli Sentler asla gerçekleşmezse (temelde paranın mantıklı olduğu yerde) para birimini Sent'i temsil edecek şekilde saklamayı seviyorum . Bu, belirtilen diğer seçeneklerden daha etkilidir.


4
Posta listelerinde, para türünün en azından tavsiye edilmediği izlenimini veren birkaç tartışma vardır, örneğin: burada: postgresql.nabble.com/Money-type-todos-td1964190.html#a1964192 plus adil olmak gerekirse: versiyon 8.2 için manuel mi diyoruz kaldırılmış: postgresql.org/docs/8.2/static/datatype-money.html
a_horse_with_no_name

11
@a_horse_with_no_name: Bağlantınız, aynı zamanda 8.2'nin mevcut sürüm olduğu ve moneytürün aslında kullanımdan kaldırıldığı 2007 tarihli bir iş parçacığına . Sorunlar giderildi ve tür, sonraki sürümlerde geri eklendi. Şahsen ben para birimini integerCents'i temsil ettiği için saklamayı seviyorum .
Erwin Brandstetter

1
Erwin, sadece veritabanı açısından doğru düşünüyor olabilirsin. Bununla birlikte, Postgresql + Java'yı birleştirirseniz, bu hiç de iyi DEĞİL (benim deneyimlerime göre). Yorumunuzu okurken, para birimi alanlarımın çoğu için MONEY kullandım ve şimdi şu Java istisnasını alıyorum: " SQLException oluştu: org.postgresql.util.PSQLException: double türü için hatalı değer: 2,500.00 ". Googledim ve iyi bir çözüm bulamadım, bu yüzden şimdi hepsini NUMERIC veya DECIMAL olarak değiştirme sıkıcı görevindeyim !!!
MD

1
@MD: Bunu duyduğuma üzüldüm ama açıkça Java için konuşmadım (ki yapamam). Hata mesajı tuhaf. "çift"? Binlik ayırıcı da bir sorun olabilir. Bununla ilgili yeni bir soru sormak isteyebilirsiniz.
Erwin Brandstetter

2
@PirateApp: Evet, kişisel favorim. Bunu söyleyerek cevabımın son cümlesini kaçırmış olabilirsiniz.
Erwin Brandstetter

70

Seçimleriniz:

  1. bigint: miktarı sent olarak saklayın. EFTPOS işlemlerinin kullandığı şey budur.
  2. decimal(12,2): miktarı tam olarak iki ondalık basamakla saklayın. Bu, çoğu genel muhasebe yazılımının kullandığı şeydir.
  3. float: berbat fikir - yetersiz doğruluk. Saf geliştiricilerin kullandığı budur.

Seçenek 2, en yaygın ve birlikte çalışması en kolay olanıdır. Hassasiyeti (örneğimde 12, toplamda 12 basamak anlamına gelir) sizin için en uygun şekilde büyük veya küçük yapın.

Bir hesaplamanın sonucu olan birden fazla işlemi (örneğin, bir döviz kurunu içeren) ticari anlamı olan tek bir değerde topluyorsanız, kesin bir makro değer sağlamak için kesinliğin daha yüksek olması gerektiğini unutmayın; gibi bir şey kullanmayı düşünün, decimal(18, 8)böylece toplam doğru olur ve tek tek değerler görüntüleme için yüzde hassasiyetine yuvarlanabilir.


10
Herhangi bir ters vergi hesaplaması veya döviz ile çalışıyorsanız, en az 4 ondalık basamağa ihtiyacınız var, yoksa veri kaybedersiniz. Yani numeric(15,4)ya numeric(15,6)iyi bir fikirdir.
Petrus Theron

3
Bir Dize kullanmak ve ana bilgisayar dilinde eşdeğer bir kayıplı olmayan ondalık tür kullanmak olan 4. bir seçenek vardır.
ioquatix

2
1000x ölçekleme faktörü ile 10000045 olarak depolanmış olsaydı, 10000.045 depolamak kesinlikle zarar vermezdi?
PirateApp

26

Tüm parasal alanlarımı şu şekilde tutuyorum:

numeric(15,6)

Bu kadar çok ondalık basamağa sahip olmak aşırı görünüyor, ancak en ufak bir şansınız olsa bile, birden fazla para birimiyle uğraşmanız gerekecek, dönüştürme için bu kadar hassaslığa ihtiyacınız olacak. Bir kullanıcıya ne sunarsam sunayım, her zaman ABD Doları'na kaydediyorum. Bu şekilde, ilgili günün dönüştürme oranı göz önüne alındığında, başka bir para birimine kolayca dönüşebilirim.

Tek bir para biriminden başka bir şey yapmazsanız, buradaki en kötü şey, sıfırları saklamak için biraz alan harcamış olmanızdır.


6
Bu, kesinti olmaması nedeniyle yanlış sonuç alma riski taşır. Sıfır olmayan değerler istemeden kalan ondalık basamaklara sızarsa, örneğin 0,333333 dolar içeren bir fiyat alanı varsa, sistemin her biri 0,33 ABD dolarından 3 öğe satın alan birinin sonucunu göstermesi durumunda, toplamda 0,99 ABD doları yerine 1,00 ABD dolarına kadar çıkabilirsiniz.
Peteris

1
Perteris, onun yerine ne önerirsin? Bu yuvarlamada ne kadar hassas olursanız olun bir sorun olabilir. Bu ideal olmasa bile daha iyi bir yol bulamadım.
Michael Collette

3
Sabit nokta ve uygun olan yerlerde kırpın. Bir müşteriye teklif edilen fiyat gibi "depolanabilir" bir para değerine ulaşır ulaşmaz, uygun ölçüler içinde olmalıdır ve bu, çoğu durumda standart perakende ortamında tam sent olarak olacaktır. Farklı iş ihtiyaçlarınız varsa (örneğin, birim başına yüksek hacimli malların fiyatı) farklı bir doğruluk ayarı olabilir, ancak sunumu depolamayla birlikte ele almalısınız - para numarasını x ondalıklı olarak görüntülerseniz (veya tam tersi, bütün binlerce mesela) o zaman da onu saklamanız gerekir o az değil, aynı zamanda artık, doğruluk.
Peteris

Çalışabilecek perakende ile ilgili birçok site için. Birlikte çalıştığım ana projede, bir tarafın aynı maliyeti bir para biriminde, bir müşteriyle başka bir para biriminde, bir tedarikçi için 3. sırada görmesi gerekebilir.
Michael Collette

19

Olarak depolanan 64 bitlik bir tamsayı kullanın bigint

Mikro dolar (veya benzer bir ana para birimi) kullanmanızı öneririm. Mikro, 1 milyonuncu anlamına gelir, yani 1 mikro dolar = 0,000001 dolar.

  • Kullanımı basit ve her dille uyumlu.
  • Bir sentin kesirlerini işlemek için yeterli hassasiyet.
  • Çok küçük birim başına fiyatlandırma için çalışır (reklam gösterimleri veya API ücretleri gibi).
  • Dizelerden veya sayısallardan daha küçük depolama için veri boyutu.
  • Hesaplamalar yoluyla doğruluğu korumak ve son çıktıda yuvarlama uygulamak kolaydır.

1
Bu en doğru cevaptır ve herhangi bir makul yazılım, eldeki en küçük para birimi ile ilgilenir. Tamsayılar üzerinde tüm matematiği yapmak, herhangi bir dile özgü kayan hareketle uğraşmak zorunda olmadığınız anlamına gelir.
Avamander

1
Bunu da kullanacak. Teşekkürler

Bu neden numeric(15,6)başka bir cevapta önerildi?
Juliusz Gonera

@JuliuszGonera Cevapta listelenen nedenlerden dolayı. Tam sayılar daha küçüktür ve her yerde desteklenir ve tüm matematik kesme problemlerinden kaçınır. Temelde sayısal kullanıyor ancak ondalık sayıları kaydırarak çok daha uyumlu bir tam sayıya sahip olursunuz.
Mani Gandham

1
Ah, doğru, depolamayla ilgili kısmı kaçırdım. Teşekkürler! "Kullanımı basit ve her dille uyumlu" konusunda ne yazık ki JavaScript, maksimum değerinden 1000 kat daha küçük olan 9007199254740991'e kadar tam sayıları destekler bigint. Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… var, ancak sınırlı destek (şimdilik) ve uyarılarla birlikte geliyor (örneğin, para birimi dönüştürme yaparken kolayca bir float ile çarpamazsınız) . Mikro dolar kullanarak bir JS tamsayısında depolayabileceğiniz maksimum tutarın 9 milyar dolar olduğu göz önüne alındığında, bu muhtemelen çoğu durumda hala iyidir.
Juliusz Gonera

4

BigIntPara birimini en küçük para biriminde parasal değeri temsil eden pozitif bir tam sayı olarak saklamak için kullanın (ör. 1,00 $ depolamak için 100 sent veya ¥ 100 (Japon yeni, sıfır ondalık para birimi) depolamak için 100 sent. Stripe bunu yapar - bir küresel e-ticaret için en önemli finansal hizmet şirketleri.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.