PHPとMySqlで金額を処理する方法は?


16

MySQLデータベースの上にPHPで記述されたレガシーコードの巨大な山を継承しました。私が気づいたのは、アプリケーションdoublesがデータの保存と操作に使用することです。

今、私doubleは丸め誤差のために金銭的操作に適していない方法について言及している多くの投稿に出会いました。しかし、PHPコードで金銭的な値を処理し、MySQLデータベースに保存する方法の完全なソリューションにまだ出会っていません。

PHPで具体的にお金を処理する場合のベストプラクティスはありますか?

私が探しているものは:

  1. データベースにどのようにデータを保存する必要がありますか?列タイプ?サイズ?
  2. データが通常の加算、減算でどのように処理されるべきか。乗算または除算?
  3. いつ値を丸めるべきですか?ある場合、どの程度の丸めが許容されますか?
  4. 大きな金額と低い金額の処理に違いはありますか?

注:日常生活で金銭的価値に遭遇する可能性 のある非常に単純化されたサンプルコード(単純化のためにさまざまなセキュリティ上の懸念は無視されました。もちろん、実際の生活ではこのようなコードは使用しません):

$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?

もちろん、このサンプルコードを使用して、より多くのユースケースを引き出す手段として使用してください。

ボーナスに関する質問 DoctrineやPROPELなどのORMを使用する場合、コードでお金を使用することはどの程度異なりますか。


1
私はPHPを知っているが、専門用語を知ることは、あなたが探している用語がで応答をGoogleに「任意精度」で、グーグル-FUのためにこれらのシナリオでは非常に貴重ですしていないphp.net/manual/en/book.bc.php
ジミー・ホッファ

1
@Jimmy Hoffa:一般に、任意精度は必要なものではなく、小数を正確に表現して操作できるものです。もちろん、両方を組み合わせて使用​​することもよくありますが、たとえば、decimalC#の型は精度に制限がありますが、金額に完全に適しています。
マイケルボルグ

1
@MichaelBorgwardtそうですね、有理型は忘れてしまいます。なぜなら、多くの言語には有理型がないからです。良い電話。
ジミー・ホッファ

通貨とレガシーコードで多くの作業を行ったので、整数で保存し、ドルではなくセントを使用します。ただし、32個の整数は驚くほど少ない量を保持できることに注意してください。
ピーターB

余談ですが、価格と割引がPOSTされていることを示すサンプルコードは私を怖がらせます。実際の使用法を反映しないように単純化されていることを願っていますが、額面では、隠しフィールドなどをPOSTしてアイテムの価格を伝えるためにブラウザを信頼しているようです。
Carson63000

回答:


6

PHP / MySQLで数値を処理するのは非常に難しい場合があります。decimal(10,2)を使用し、数値がより長いか、より高い精度である場合、エラーなしで切り捨てられます(dbサーバーに適切なモードを設定しない限り)。

大きな値または高精度の値を処理するには、BCMathなどのライブラリを使用できます。これにより、大きな数値で基本的な操作を行い、必要な精度を維持できます。

正確にどのような計算を行うかはわかりませんが、プロセスを通して適切な精度を使用しない場合、(0.22 * 0.4576)+(0.78 * 0.4576)は0.4576に等しくないことにも留意する必要があります。

MySQLのDECIMALの最大サイズは65であるため、どのような目的にも十分すぎるはずです。DECIMALフィールドタイプを使用する場合、ORMまたは単純なPDO / mysql(i)の使用に関係なく、文字列として返されます。

データベースにどのようにデータを保存する必要がありますか?列タイプ?サイズ?

必要な精度を備えたDECIMAL。為替レートを使用している場合は、少なくとも小数点以下4桁が必要です。

データが通常の加算、減算でどのように処理されるべきか。乗算または除算?

BCMathを使用して保存側にし、なぜfloatを使用するのが得策ではないのか

いつ値を丸めるべきですか?ある場合、どの程度の丸めが許容されますか?

通貨の値については、通常、小数点以下2桁が許容されますが、たとえば為替レートを使用している場合は、さらに必要になる場合があります。

大きな金額と低い金額の処理に違いはありますか?

あなたが何を意味するかに依存します。数値を高精度で処理することには間違いなく違いがあります。


9

簡単な回避策は、整数として保存することです。99.99は9999として保存されます。これが機能しない場合(そして、これが悪い選択である理由はたくさんあります)、Decimal型を使用できます。mysql側のhttp://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html php側では、この/programming/3244094/decimal-type-in​​-phpが見つかりました。

ボーナス質問:言いにくい。ormは、選択したデータ型に基づいて機能します。私はあなたが助けるために抽象化でいくつかのことをすることができると言うでしょうが、この特定の問題は単にORMに移動することによって対処されていません。


1
FWIW、Drupal Commerceは9999トリックを使用して価格を保存します。
フロリアンマーゲイン

1
「そして、これが悪い選択である理由はたくさんありますこの実践の問題点をいくつか教えていただけますか?
ソンゴ


@MichaelBorgwardt情報をありがとう、ソンゴは遅れてごめんなさい、ハリケーンは私たちを連れ出しました:)
オミナス

3

私はこれに私の経験を入れようとします:

私が探しているものは:

データベースにどのようにデータを保存する必要がありますか?列タイプ?サイズ?

私はDECIMAL(10,2)mysqlに問題なく使用してきました(8個の完了と2個の小数== 99.999.999,99 ==膨大な量)が、これはカバーする必要のあるお金の範囲に依存します。膨大な量は、特に注意して使用する必要があります(OSの最大フロート値など)。小数部分では、値の切り捨てや丸めを回避するために2つの値を使用します。お金を取って、より多くの小数が必要なケースはほとんどありません(その場合、ユーザーがそれらのすべてを使用することを確認する必要があります。

データが通常の加算、減算でどのように処理されるべきか。乗算または除算?

1つの通貨と交換テーブル(日付付き)を使用します。これにより、常に正しい金額が保存されるようになります。追加:完全な値を保存し、計算結果を含むビューを作成します。これは、その場で値を修正するのに役立ちます

いつ値を丸めるべきですか?ある場合、どの程度の丸めが許容されますか?

繰り返しますが、システムのお金の範囲に依存します。通貨交換の混乱に陥る必要がない限り、常にKISSの用語で考えてください

大きな金額と低い金額の処理に違いはありますか?

OSとプログラミング言語に応じて、最大値と最小値を常に確認する必要があります


答えてくれてありがとう、でもifのようなものを処理する$valueToBeStored= $a * $b;必要が$aあり$b、両方がデータベースから10進数として読み取られる場合、それらはdoublePHP でキャストされると思いますか?それは数字に影響しますか?
松o

$a$bdbから取られていますか?したがって、私の例では、$valueToBeStored常にソース$a$bデータがあるため、保存する必要はありません。そのため、関数などでプログラムで値を処理したり、列の結果を使用してmysqlビューを作成したりできます。このように、値を変更する必要がある場合、複数の場所を変更することを心配する必要はありません(エラーが発生しやすい)
Alwin Kesler
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.