如何设计数据库时间、金额与统计口径
金额不是普通数字,月报也不是一条临时 SQL。
| 事故 | 根因 | 设计动作 |
| 5 月消费算进 6 月。 | 月报按 created_at 算。 | 业务发生时间和系统写入时间分开。 |
| 本地 5 月 31 日,UTC 已是 6 月。 | 没有报表时区。 | 账本保存 reporting timezone。 |
| 199.99 汇总成 200.00。 | 金额用浮点或舍入不统一。 | 用最小货币单位或统一 DECIMAL。 |
| 100 美元和 100 日元直接相加。 | 没有币种和汇率快照。 | 保存原始币种、折算币种和汇率来源。 |
| 退款和转账被当消费。 | 只看金额正负。 | 保存业务类型和报表影响。 |
判断:数字不可信,通常不是 SQL 写错,而是 schema 没保存业务语义。
| 问题 | 最低设计 |
| 这笔钱什么时候发生? | occurred_at 和 created_at 分开。 |
| “本月”按谁的时区算? | 账本有明确报表时区和业务日期。 |
| 金额单位是什么? | 统一最小单位或 DECIMAL,禁止浮点金额。 |
| 原始币种和折算币种是什么? | 保存原始金额、折算金额、汇率来源和时间。 |
| 退款、转账、调整算不算消费? | entry_type 和 reporting_effect 分开。 |
ledger_entries:
occurred_at, occurred_on, occurred_tz, created_at
original_amount_minor, original_currency
reporting_amount_minor, reporting_currency
exchange_rate, rate_source, rate_time
entry_type, direction, reporting_effect, metric_version
| 样本 | 通过标准 |
| 5 月发生、6 月补录。 | 进入 5 月月报,created_at 只用于审计。 |
| 跨时区月底。 | 按账本报表时区归属月份。 |
| 小数金额聚合。 | 结果稳定,没有浮点误差。 |
| 跨币种报表。 | 历史报表使用快照汇率。 |
| 退款和转账。 | 退款能冲减,转账不变成收入支出。 |
记住:时间、金额、币种、统计口径都要入库成语义,不能让 SQL 临时解释。