PostgreSQL: Datatype mana yang harus digunakan untuk Mata Uang?

128

Sepertinya Moneytipe tidak disarankan seperti yang dijelaskan di sini

Aplikasi saya perlu menyimpan mata uang, tipe data apa yang harus saya gunakan? Numerik, Uang, atau FLOAT?

pelamun
sumber
7
Jika Anda telah membaca seluruh utas, Numeric adalah jalan yang harus ditempuh.
razpeitia
Bagi siapa pun yang bekerja dengan banyak mata uang dan peduli tentang menyimpan kode mata uang selain jumlah, Anda mungkin ingin melihat pemodelan mata uang dalam database (SO) dan ISO 4217 (Wikipedia). Jawaban singkatnya adalah Anda membutuhkan dua kolom.
Fabien Snauwaert
Jangan gunakan uang
a_horse_with_no_name

Jawaban:

90

Numerik dengan presisi 2 unit paksa. Jangan pernah menggunakan float atau float like datatype untuk mewakili mata uang karena jika Anda melakukannya, orang-orang akan tidak senang ketika angka garis bawah laporan keuangan salah dengan + atau - beberapa dolar.

Jenis uang hanya tersisa untuk alasan historis sejauh yang saya tahu.

Chris Farmiloe
sumber
9
Itu bukan alasan Anda menghindari floating point. Bahkan Numeric akan memiliki kesalahan pembulatan jika Anda membaginya dengan apa pun yang tidak dibagi menjadi kekuatan sepuluh, tidak peduli apa pun presisi yang Anda gunakan. (Bagaimanapun, Presisi 2 adalah Ide yang Buruk ... periksa dokumen.)
Doradus
6
Jika Anda ingin mendukung mata uang sewenang-wenang, jangan pernah membuat skala sama dengan 2 (dalam istilah postgresql, presisi adalah jumlah semua digit, misalnya dalam 121,121 sama dengan 6). Ada mata uang, seperti Bahrain Dinar, di mana 1000 sub-unit sama dengan satu unit, dan ada mata uang yang tidak memiliki sub-unit sama sekali.
Nikolay Arhipov
@NikolayArhipov poin bagus, jadi maksnya sebenarnyascale - precision
Konrad
1
numeric(3,2)akan dapat menyimpan maks9.99 3-2 = 1
Konrad
5
Jangan lakukan ini! Kecuali jika Anda berencana untuk mengalikan dengan 100 sebelum memuat nilai apa pun ke bahasa lain dan kemudian melakukan matematika dengan bilangan bulat - Anda akan mendapatkan hasil yang salah. Simpan barang dalam sen (unit mata uang terkecil yang Anda hadapi) dan hemat diri Anda dari kerepotan. Jawaban yang sangat buruk dalam banyak kasus.
Avamander
111

Sumber Anda sama sekali tidak resmi. Itu tanggal 2011 dan saya bahkan tidak mengenali penulis. Jika jenis uang secara resmi "berkecil hati" PostgreSQL akan mengatakannya di manual - yang tidak .

Untuk sumber yang lebih resmi , baca utas ini di pgsql-jenderal (mulai minggu ini saja!) , Dengan pernyataan dari pengembang inti termasuk D'Arcy JM Cain (penulis asli jenis uang) dan Tom Lane:

Jawaban terkait (dan komentar!) Tentang peningkatan dalam rilis terbaru:

Pada dasarnya, moneypenggunaannya sangat terbatas. The Postgres Wiki menyarankan untuk sebagian besar menghindarinya, kecuali untuk kasus-kasus yang didefinisikan secara sempit. Keuntungan lebih numericadalah kinerja .

decimalhanyalah alias untuk numericdi Postgres, dan banyak digunakan untuk data moneter, menjadi tipe "arbitrary precision". Manual :

Tipe ini numericdapat menyimpan angka dengan jumlah digit yang sangat besar. Sangat disarankan untuk menyimpan jumlah moneter dan jumlah lainnya di mana ketelitian diperlukan.

Secara pribadi, saya suka menyimpan mata uang sebagai integermewakili Sen jika sen pecahan tidak pernah terjadi (pada dasarnya di mana uang masuk akal). Itu lebih efisien daripada opsi lain yang disebutkan.

Erwin Brandstetter
sumber
4
Ada beberapa diskusi di milis yang memberi kesan bahwa jenis uang setidaknya tidak direkomendasikan, misalnya: di sini: postgresql.nabble.com/Money-type-todos-td1964190.html#a1964192 plus bersikap adil: manual untuk versi 8.2 memang menyebutnya usang: postgresql.org/docs/8.2/static/datatype-money.html
a_horse_with_no_name
11
@a_horse_with_no_name: Tautan Anda ke utas dari tahun 2007, yang juga merupakan versi 8.2 saat ini dan moneyjenisnya sebenarnya sudah tidak digunakan lagi. Masalah telah diperbaiki dan jenisnya telah ditambahkan kembali di versi yang lebih baru. Secara pribadi saya suka menyimpan mata uang sebagai integermewakili Sen.
Erwin Brandstetter
1
Erwin, Anda mungkin berpikir benar dari perspektif basis data saja. Namun, jika Anda menggabungkan Postgresql + Java, itu TIDAK baik sama sekali (dari pengalaman saya). Membaca komentar Anda, saya menggunakan MONEY untuk sebagian besar bidang mata uang saya dan sekarang saya mendapatkan pengecualian Java ini: " SQLException terjadi: org.postgresql.util.PSQLException: Nilai buruk untuk tipe ganda: 2,500.00 ". Saya telah mencari di Google dan tidak menemukan solusi yang baik, jadi saya menjadi tugas yang membosankan untuk mengubah semuanya menjadi NUMERIC atau DECIMAL sekarang !!!
MD
1
@ MD: Maaf mendengar itu, tapi saya jelas tidak berbicara untuk Java (yang saya tidak bisa). Pesan kesalahannya aneh. "dua kali lipat"? Dan pemisah ribuan mungkin juga menjadi masalah. Anda mungkin ingin memulai pertanyaan baru tentang itu.
Erwin Brandstetter
2
@ PirateApp: Ya, favorit pribadi saya. Anda mungkin telah melewatkan kalimat terakhir dari jawaban saya, hanya mengatakan itu.
Erwin Brandstetter
68

Pilihan Anda adalah:

  1. bigint: menyimpan jumlah dalam sen. Inilah yang digunakan transaksi EFTPOS.
  2. decimal(12,2): menyimpan jumlah dengan tepat dua tempat desimal. Ini yang digunakan perangkat lunak buku besar paling umum.
  3. float: ide yang mengerikan - akurasi tidak memadai. Inilah yang digunakan pengembang naif.

Opsi 2 adalah yang paling umum dan paling mudah untuk digunakan. Jadikan presisi (12 dalam contoh saya, artinya 12 digit semuanya) besar atau kecil sebaik mungkin bagi Anda.

Perhatikan bahwa jika Anda menggabungkan beberapa transaksi yang merupakan hasil perhitungan (misalnya melibatkan nilai tukar) menjadi nilai tunggal yang memiliki makna bisnis, ketelitiannya harus lebih tinggi untuk memberikan nilai makro yang akurat; pertimbangkan untuk menggunakan sesuatu seperti decimal(18, 8)sehingga jumlahnya akurat dan nilai individual dapat dibulatkan menjadi presisi sen untuk tampilan.

Orang Bohemian
sumber
10
Jika Anda bekerja dengan segala jenis perhitungan pajak balik atau pertukaran mata uang asing, Anda memerlukan setidaknya 4 tempat desimal, atau Anda akan kehilangan data. Jadi numeric(15,4)atau numeric(15,6)itu ide yang bagus.
Petrus Theron
3
Ada opsi ke-4 - yaitu menggunakan String dan menggunakan jenis desimal non-lossy yang setara dalam bahasa host.
ioquatix
2
bagaimana dengan integer berskala, pasti menyimpan 10000.045 tidak ada salahnya jika disimpan sebagai 10000045 dengan faktor penskalaan 1000x?
PirateApp
26

Saya menyimpan semua bidang moneter saya sebagai:

numeric(15,6)

Tampaknya berlebihan untuk memiliki banyak tempat desimal, tetapi jika ada peluang sekecil apa pun Anda harus berurusan dengan banyak mata uang, Anda akan membutuhkan ketelitian yang tinggi untuk mengkonversi. Tidak peduli apa yang saya presentasikan kepada pengguna, saya selalu menyimpan ke US Dollar. Dengan cara itu saya dapat dengan mudah mengkonversi ke mata uang lain, mengingat tingkat konversi untuk hari yang terlibat.

Jika Anda tidak pernah melakukan apa pun kecuali satu mata uang, hal terburuk di sini adalah Anda menyia-nyiakan sedikit ruang untuk menyimpan beberapa angka nol.

Michael Collette
sumber
6
Ini memiliki risiko hasil yang salah karena kurangnya pemotongan. Jika nilai bukan nol secara tidak sengaja bocor ke tempat desimal yang tersisa, misalnya, bidang harga yang berisi 0,333333 dolar, maka Anda dapat memiliki situasi di mana sistem menunjukkan hasil seseorang membeli 3 item seharga $ 0,33 masing-masing, berjumlah hingga $ 1,00 bukannya $ 0,99.
Peteris
1
Perteris, jadi apa yang Anda sarankan sebagai gantinya? Tidak peduli berapa banyak presisi yang Anda berikan pada pembulatan ini bisa menjadi masalah. Saya belum menemukan cara yang lebih baik, bahkan jika ini tidak ideal.
Michael Collette
3
Memperbaiki titik dan memotong jika perlu. Segera setelah Anda mencapai nilai uang "yang dapat disimpan" misalnya harga yang ditawarkan kepada pelanggan, itu harus dalam metrik yang sesuai, yang dalam banyak kasus akan berada dalam sen keseluruhan di lingkungan ritel standar. Jika Anda memiliki kebutuhan bisnis yang berbeda (misalnya harga barang bervolume tinggi per unit) mungkin ada pengaturan akurasi yang berbeda, tetapi Anda harus memperlakukan presentasi bersama dengan penyimpanan - jika Anda menampilkan nomor uang dengan x desimal (atau sebaliknya, misalnya di seluruh ribuan) maka Anda juga harus menyimpannya dengan yang akurasi, tidak kurang tetapi juga tidak lebih.
Peteris
Untuk banyak situs terkait ritel yang mungkin berfungsi. Proyek utama saya bekerja dengan mungkin memiliki satu pihak yang perlu melihat biaya yang sama dalam satu mata uang, dengan klien dalam mata uang lain, untuk pemasok dalam 3 belum.
Michael Collette
19

Gunakan integer 64-bit yang disimpan sebagai bigint

Saya sarankan menggunakan dolar mikro (atau mata uang utama serupa). Mikro berarti 1 juta jadi 1 mikro-dolar = $ 0,000001.

  • Mudah digunakan dan kompatibel dengan setiap bahasa.
  • Cukup presisi untuk menangani pecahan sen.
  • Bekerja untuk harga per unit yang sangat kecil (seperti tayangan iklan atau biaya API).
  • Ukuran data yang lebih kecil untuk penyimpanan daripada string atau angka.
  • Mudah menjaga akurasi melalui perhitungan dan menerapkan pembulatan pada hasil akhir.
Mani Gandham
sumber
1
Ini adalah jawaban yang paling benar dan setiap perangkat lunak yang masuk akal berurusan dengan unit mata uang terkecil di tangan. Melakukan semua matematika pada bilangan bulat berarti Anda tidak harus berurusan dengan mangat float khusus bahasa.
Avamander
1
Akan menggunakannya juga. Terima kasih
Mengapa ini lebih numeric(15,6)disarankan dalam jawaban lain?
Juliusz Gonera
@JuliuszGonera Untuk alasan yang tercantum dalam jawaban. Bilangan bulat lebih kecil dan didukung di mana-mana, dan menghindari semua masalah pemotongan matematika. Ini pada dasarnya menggunakan angka tetapi menggeser desimal sehingga Anda memiliki bilangan bulat yang jauh lebih kompatibel.
Mani Gandham
1
Ah, benar, saya melewatkan bagian tentang penyimpanan. Terima kasih! Mengenai "Mudah digunakan dan kompatibel dengan setiap bahasa" sayangnya JavaScript mendukung bilangan bulat hingga 9007199254740991 yang lebih dari 1000x lebih kecil dari nilai maks bigint. Ada developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… tetapi ia datang dengan dukungan terbatas (untuk saat ini) dan peringatan (mis. Anda tidak dapat melipatgandakannya dengan float dengan mudah saat melakukan konversi mata uang) . Mengingat bahwa maks Anda dapat menyimpan dalam integer JS menggunakan dolar mikro adalah $ 9 miliar yang mungkin masih bagus untuk kebanyakan kasus.
Juliusz Gonera
3

Gunakan BigIntuntuk menyimpan mata uang sebagai bilangan bulat positif yang mewakili nilai moneter dalam unit mata uang terkecil (misalnya, 100 sen untuk menyimpan $ 1,00 atau 100 untuk menyimpan ¥ 100 (yen Jepang, mata uang nol desimal). Inilah yang dilakukan Stripe - satu perusahaan jasa keuangan terpenting untuk e-commerce global.

Max Hodges
sumber