怎樣在資料庫中儲存貨幣

Epona發表於2019-08-02

在我們日常開發的過程中,一定會碰上和貨幣打交道的程式碼。那麼這時候就會有一個問題?我們怎樣在資料庫中儲存貨幣單位呢?

使用整數型別

說實話,我到現在也是使用的這種方法。以分為單位,配合 Laravel Eloquent 中的 修改器 功能,可以很輕鬆的實現我想要的東西。

    public function setPriceAttribute($value)
    {
        $this->attributes['price'] = intval($value * 100);
    }

    public function getPriceAttribute($value)
    {
        return $value / 100;
    }

當然,專案裡的需求對精度沒有那麼高的要求,使用這種設計能符合我的需求。如果有更復雜的貨幣需求,這種設計可能就無法滿足了。

使用 Monetary types 型別

注意這個方法只適合 Postgresql 資料庫。有關介紹可以檢視官方文件

舉個栗子

[local] =# CREATE TABLE money_example (cash money);
[local] =# INSERT INTO money_example VALUES ('$99.99');
[local] =# INSERT INTO money_example VALUES (99.99);
[local] =# INSERT INTO money_example VALUES (99.98996998);

[local] =# SELECT * FROM money_example;

 cash
------------
  $99.99
  $99.99
  $99.99
(3 row)

從上面的例子可以看出資料庫預設會為我們保留2位小數(四捨五入)。

看似很美好,是不是?但是有一個最大的問題是,我們只能使用一種貨幣單位,即要麼是美元,要麼是人民幣等,無法混著使用。如果只用一種貨幣的話,那麼可以考慮這種方法。

[local] =# INSERT INTO money_example VALUES ('¥99.99');
ERROR:  22P02: invalid input syntax for type money: "¥99.99"
LINE 1: INSERT INTO money_example VALUES ('¥99.99');

使用 decimal 型別

講道理的話,這個應該是最正確的用法了。直接上栗子。

[local] =# CREATE TABLE numeric_example ( numeric_cash NUMERIC(6, 4) );
[local] =# INSERT INTO numeric_example VALUES('99.9999');
[local] =# INSERT INTO numeric_example VALUES('99.998550');
[local] =# INSERT INTO numeric_example VALUES('99.998549');
[local] =# SELECT * FROM numeric_example;
  numeric_cash
---------------
  99.9999
  99.9986
  99.9985
(3 row)

在上面的例子中我們設定了6位數的長度,並且4位是小樹。當我們插入的資料長度不符合規格的時候就會丟擲錯誤(即,小數點前面的位數大於2的時候)。

[local] =# INSERT INTO numeric_example VALUES('999.99850');
ERROR:  22003: numeric field overflow
DETAIL:  A field with precision 6, scale 4 must round to an absolute value less than 10^2.
LOCATION:  apply_typmod, numeric.c:5998

參考文章

結語

以後在開發新專案的時候,我應該會使用最後一種方法來進行貨幣儲存。

不知道大家在開發中用的是哪種方法呢?

There's nothing wrong with having a little fun.

相關文章