PHP ve MySql'de parasal değerler nasıl ele alınır?


16

Ben bir MySQL veritabanı üstünde PHP yazılmış eski kod büyük bir yığın miras. Fark ettiğim şey, uygulamanın doublesverilerin depolanması ve işlenmesi için kullandığı .

Şimdi double, yuvarlama hataları nedeniyle parasal operasyonlar için nasıl uygun olmadığını belirten çok sayıda gönderiyle karşılaştım. Ancak, henüz parasal değerlerin PHP kodunda ele alınması ve bir MySQL veritabanında saklanması için tam bir çözümle karşılaşmadım.

Özellikle PHP'de para işleme konusunda en iyi uygulama var mı?

Aradığım şeyler:

  1. Veriler veritabanında nasıl saklanmalıdır? sütun türü? boyut?
  2. Veriler normal toplama, çıkarma işlemlerinde nasıl olmalıdır? çarpma veya bölme?
  3. Değerleri ne zaman yuvarlamalıyım? Varsa ne kadar yuvarlama kabul edilebilir?
  4. Büyük parasal değerlerle düşük değerlerin ele alınması arasında bir fark var mı?

Not: Bir ÇOK I (. Çeşitli güvenlik kaygıları basitleştirme için gözardı edildi gerçek hayatta Elbette böyle kodumu kullanmak asla) günlük yaşamda para değerleri karşılaşabileceğiniz nasıl örnek kod basitleştirilmiş:

$a= $_POST['price_in_dollars']; //-->(ex: 25.06) will be read as a string should it be cast to double?
$b= $_POST['discount_rate'];//-->(ex: 0.35) value will always be less than 1
$valueToBeStored= $a * $b; //--> any hint here is welcomed 

$valueFromDatabase= $row['price']; //--> price column in database could be double, decimal,...etc.

$priceToPrint=$valueFromDatabase * 0.25; //again cast needed or not?

Umarım bu örnek kodu daha fazla kullanım durumu ortaya çıkarmak ve elbette tam anlamıyla almamak için bir araç olarak kullanırsınız.

Bonus Soru Doktrin veya PROPEL gibi bir ORM kullanacaksam, kodumda para kullanmak ne kadar farklı olacak.


1
Ben PHP biliyorum ama terminoloji bilerek google-fu için bu senaryolarda ölçülemez yok, aradığınız terimi ile yanıt verir google hangi "keyfi hassas" olduğunu php.net/manual/en/book.bc.php
Jimmy Hoffa

1
@Jimmy Hoffa: arbiter hassasiyet genellikle ihtiyacınız olan şey değildir, ondalık kesirleri doğru bir şekilde temsil edebilen ve onlarla çalışabilen bir şeydir. Tabii ki, her ikisini de sık sık kombine bulursunuz, ancak örneğin decimalC # 'daki tipin kesinliği sınırlıdır, ancak parasal değerler için mükemmeldir.
Michael Borgwardt

1
@MichaelBorgwardt doğru, rasyonel türleri unuturum çünkü pek çok dilde yoktur. İyi karar.
Jimmy Hoffa

Para birimleri ve eski kodlarla çok çalıştıktan sonra, tamsayılarda saklamak ve dolar yerine sent kullanmak için gidin derim. Bununla birlikte, 32 tamsayı şaşırtıcı derecede küçük bir miktarda tutabilir. 4) 32 bitlik bir tam sayı, imzalanmamış ve sentler kullanıyorsa yalnızca 21 milyon tutarı tutabilir.
Pieter B

Bir kenara, POSTed fiyat ve indirimler gösteren örnek kod beni korkutuyor. Umarım o kadar basittir ki gerçek kullanımı yansıtmaz, ancak yüz değerinde, gizli bir alanı veya bazılarını POST yaparak bir öğenin fiyatının ne olduğunu söylemek için tarayıcıya güveniyor gibi görünüyorsunuz.
Carson63000

Yanıtlar:


6

PHP / MySQL ile sayıları işlemek oldukça zor olabilir. Ondalık (10,2) kullanırsanız ve numaranız daha uzunsa veya daha yüksek hassasiyete sahipse, hata olmadan kesilir (db sunucunuz için uygun modu ayarlamadığınız sürece).

Büyük değerleri veya yüksek hassasiyet değerlerini işlemek için BCMath gibi kitaplığı kullanabilirsiniz , büyük sayılar üzerinde temel işlem yapmanıza ve gerekli hassasiyeti korumanıza izin verir.

Tam olarak hangi hesaplamaları yapacağınızdan emin değilim, ancak süreç boyunca uygun hassasiyeti kullanmayacaksanız (0.22 * 0.4576) + (0.78 * 0.4576) 'ın 0.4576'ya eşit olmayacağını da aklınızda bulundurmalısınız.

MySQL'de maksimum DECIMAL boyutu 65'tir, bu nedenle herhangi bir amaç için fazlasıyla yeterli olmalıdır. DECIMAL alan türünü kullanırsanız, ORM veya yalnızca düz PDO / mysql (i) kullanılmasına bakılmaksızın dize olarak döndürülür.

Veriler veritabanında nasıl saklanmalıdır? sütun türü? boyut?

İhtiyacınız olan hassasiyetle DECIMAL. Döviz kurları kullanıyorsanız en az dört ondalık basamağa ihtiyacınız olacaktır

Veriler normal toplama, çıkarma işlemlerinde nasıl olmalıdır? çarpma veya bölme?

Tasarruflu olmak için BCMath kullanın ve şamandıra kullanmak neden iyi bir fikir olmayabilir

Değerleri ne zaman yuvarlamalıyım? Varsa ne kadar yuvarlama kabul edilebilir?

Parasal değerler için normal iki ondalık basamak kabul edilebilir, ancak örneğin döviz kurları kullanıyorsanız daha fazlasına ihtiyacınız olabilir.

Büyük parasal değerlerle düşük değerlerin ele alınması arasında bir fark var mı?

Geniş anlamda ne demek istediğinize bağlı. Rakamların yüksek hassasiyetle kullanılması arasında kesinlikle bir fark vardır.


9

Basit bir çözüm, onları tamsayı olarak saklamaktır. 99.99 9999 olarak depolanır. Bu işe yaramazsa (ve bunun kötü bir seçim olabilmesinin birçok nedeni varsa) Ondalık türünü kullanabilirsiniz. mysql tarafında http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html . Php tarafında ben sonra ne olabilir bu /programming/3244094/decimal-type-in-php buldum .

Bonus soru: Söylemesi zor. Orm, seçilen veri türlerine göre çalışacaktır. Yardım etmek için soyutlama ile bazı şeyler yapabileceğinizi söyleyebilirim, ancak bu özel sorun sadece bir ORM'ye geçerek ele alınmadı.


1
FWIW, Drupal Commerce, fiyatlarını depolamak için 9999 numarasını kullanıyor.
Florian Margaine

1
"ve bunun kötü bir seçim olabilmesinin birçok nedeni var" Bu uygulamanın bazı problemlerini paylaşır mısınız?
Songo

2
@Songo: bkz. Yüzen
Michael

@MichaelBorgwardt Bilgi için teşekkürler, Songo gecikme için özür dilerim, kasırga bizi dışarı çıkardı :)
Ominus

3

Deneyimimi buna koymaya çalışacağım:

Aradığım şeyler:

Veriler veritabanında nasıl saklanmalıdır? sütun türü? boyut?

Ben DECIMAL(10,2)mysql için sorunsuz (8 tamamlar ve 2 ondalık == 99.999.999,99 == büyük miktarda) kullanıyorum, ama bu kapsam gerekir para aralığına bağlıdır. Büyük miktarlar ekstra dikkatle alınmalıdır (örneğin, OS maksimum şamandıra değerleri). Ondalık kısımda kesme veya yuvarlama değerlerinden kaçınmak için 2 değer kullanıyorum. Parayla ilgili olarak, daha fazla ondalık sayıya ihtiyacınız olan birkaç durum vardır (bu durumda kullanıcının hepsiyle çalışacağından emin olmanız gerekir, aksi takdirde işe yaramaz verilerdir)

Veriler normal toplama, çıkarma işlemlerinde nasıl olmalıdır? çarpma veya bölme?

Bir para birimi ve bir değişim tablosu (tarihlerle birlikte) ile çalışın. Bu şekilde, her zaman doğru miktarda kaydedilmiş olacağınızdan emin olursunuz. Ekstra: tam değerleri kaydedin ve hesaplama sonuçlarıyla bir görünüm oluşturun. Bu, değerleri anında düzeltmenize yardımcı olacaktır

Değerleri ne zaman yuvarlamalıyım? Varsa ne kadar yuvarlama kabul edilebilir?

Yine, sisteminizin para aralığına bağlıdır. Döviz bozdurma karmaşasına düşmemeniz durumunda her zaman KISS terimleriyle düşünün

Büyük parasal değerlerle düşük değerlerin ele alınması arasında bir fark var mı?

İşletim sisteminize ve programlama dillerinize bağlı olarak her zaman maksimum ve minimum değerleri kontrol etmeniz gerekir


Cevabınız için teşekkürler, ama eğer $valueToBeStored= $a * $b;if $ave $bher ikisi de veritabanından ondalık olarak okundu gibi bir şey işlemek gerekiyorsa ben onlar doublePHP PHP atılacak düşünüyorum ? bu sayıları etkiler mi?
Songo

$ave $bdb alınır? yani, örneğimde saklamanız gerekmiyor $valueToBeStoredçünkü her zaman kaynağa $ave $bverilere sahip olacaksınız . Böylece, bir işlevdeki değeri programlı olarak çalıştırabilir veya sütun sonucuyla bir mysql görünümü oluşturabilirsiniz. Bu şekilde, herhangi bir değerin değiştirilmesi gerekiyorsa, birden fazla yeri değiştirme konusunda endişelenmenize gerek yoktur (hataya meyilli)
Alwin Kesler
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.