通貨型の取り扱い
- 業務で四則演算を通貨に関する項目で行う際には、切り捨て、繰り上げの問題をどうするかとなります。
消費税計算が投入された際に結構問題になったのは老人たちの記憶にもしっかりあるでしょう。 - 秤量単位で扱われる商品では、小数点以下の単価設定されるケースが多いのでRDBMで通貨型がある場合、
他のDB実装との整合性を考えずに導入すると問題になることがあります。
歴史っぽいもの
- MYSQL系,sqlite,firebird, などの軽量実装を旨とするRDBMでは、通貨型を実装していません。電子取引の長い慣行を保持しているホスト系システム=COBOLから移行を念頭にしたDB/2,Oracle,Microsoft-SQLserver(旧くはSybase(→2010年にSAPが買収) Ver6.5以降)などは、予め定義された型として実装することになったと思われます。(推測)
- ここではpostgresqlで通貨型を選択した場合、出てくる問題について書きます。
名称 = money 型
- CURRENCY "型" は VBAなどの 言語の型名で混乱します。VBAと密接な関係の JetDB のファイルを mdbtools/mdb-schema で処理すると "Currency" と刻まれていることが確認できます。
- 一方で Microsoft sqlserver では "money"型 と呼ばれています。双方、開発出自が異なることがよく分かります。
精度について
money型は貨幣金額を固定精度の小数点で格納します。
「浮動小数点で扱うのは現金だけに厳禁」など🤓というか、後述の割り算問題があります。
小数点精度はデータベースのlc_monetary設定で決定されます。
ロケールの件
- 通貨型にはロケール設定があります。
lc_monetary 設定は、DBにデフォルトでは"" です。
通常は "$" になってしまいます。
sample =# select 10::money;
money
--------
$10.00
(1 行)
sample = # set lc_monetary to "ja_JP.UTF-8";
SET
# select 10::money;
money
-------
¥10
(1 行)
-
set コマンドを用いて設定することができるのですが、データ入力時に lc_monetary が "" または "C"、その他の言語で設定されていた場合、set されたロケールでの表現における小数点の扱いが変更されてしまいます。 具体的に言うと "7.00 円(円.銭単位)"で設定された単価を 700円にしてしまうなどの現象が見られるので money 型を扱う際は、DBの初期設定時には注意が必要です。
-
他のDBからのデータ移行する前に 先程の lc_monetary を設定しておけば問題回避されます。
商を求める=割り算で特定数値型にキャストされる
- 数学演算子については以下のようになっています。
sample =# SELECT
o.oprname, t1.typname AS lefttype, t2.typname AS righttype
FROM pg_operator o, pg_type t1, pg_type t2
WHERE t1.typname = 'money' AND (t1.oid = o.oprleft AND t2.oid = o.oprright);
oprname | lefttype | righttype
---------+----------+-----------
* | money | float4
/ | money | float4
= | money | money
<> | money | money
< | money | money
> | money | money
<= | money | money
>= | money | money
+ | money | money
- | money | money
* | money | float8
/ | money | float8
* | money | int8
/ | money | int8
* | money | int4
/ | money | int4
* | money | int2
/ | money | int2
/ | money | money
(19 行)
所感
- なにも考えずに設定したDBで、$単位で表示されてしまっていた場合など、他のDBから移行時に money 型設定に変更するのは注意が必要です。Currencyなどと設定されているとしても、32bit整数型かつ100倍(=銭単位の扱いに備えて)で格納して、四捨五入の処理を別途行うなどの配慮があれば、money型を使う必要ないかもしれません。
- postgresql の money型は、1円未満を取り扱わない銀行口座の預金残高などでは問題がないのですが、商業会計での「銭単位までの単価」を扱うことができない実装になっています。内部処理で補助通貨単位までは扱っているかもしれませんが、ざっと見た限りでは切り捨てられているように思われます。 少なくとも秤量バラ売りがある業界で日本円での単価などには使えない仕様になっています。