怎样在数据库中存储货币

在我们日常开发的过程中,一定会碰上和货币打交道的代码。那么这时候就会有一个问题?我们怎样在数据库中存储货币单位呢?

使用整数类型

说实话,我到现在也是使用的这种方法。以分为单位,配合 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

参考文章

结语

以后在开发新项目的时候,我应该会使用最后一种方法来进行货币存储。

不知道大家在开发中用的是哪种方法呢?

本作品采用《CC 协议》,转载必须注明作者和本文链接
There's nothing wrong with having a little fun.
Epona
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 5

decimal 类型是不是会有误差,记得之前有个项目就是用的 decimal 类型,后来发现实际金额和数据库里的金额经常会相差一两分。具体是怎么回事也没有详细排查

4年前 评论
Epona (楼主) 4年前
xiaoguo0426 4年前
Jarvis42 4年前
h-o-o (作者) 4年前
Caral 4年前
crocky 4年前
AlicFeng 4年前

cknow/laravel-money,基于 moneyphp/money 的封装, 了解一下呢
前端浮点输入 --> Money 对象 --> 整数入库
整数出库 --> Money 对象 --> 前端数组,包括了整数、货币、格式化的金额

4年前 评论
tkn_laravel_china 4年前
klgd (作者) 4年前
tkn_laravel_china 4年前
Epona

@klgd 嗯是有这种第三方的库,但是基本原理还是要了解好。

4年前 评论

使用php bc函数啊

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!