财务核算逻辑应分base→dm→rpt三层:base仅清洗原始凭证,dm补维度与基础聚合,rpt封装可读字段和稳定转换;否则易导致性能差、维护难、协作冲突。

直接说结论:财务核算逻辑不该塞进单个视图里硬扛,而应拆成 base → dm → rpt 三层,每层只做一件事——base 清洗原始凭证,dm 补全维度与基础聚合,rpt 按报表口径封装可读字段和稳定转换;否则一改就崩、一查就慢、多人协作必打架。
base_ 视图该做什么、不该做什么
base_ 层是财务数据的“原始底片”,只负责把凭证表、科目余额表、辅助核算表等原始数据做最小干预清洗。
- 必须做:去重(
DISTINCT ON (voucher_id, line_no))、空值归一(COALESCE(debit_amt, 0))、字段重命名(voucher_date AS doc_date)、类型强制(CAST(voucher_no AS TEXT)) - 禁止做:任何 JOIN(哪怕只是关联合并口径表)、任何
GROUP BY、任何业务状态判断(如CASE WHEN voucher_type IN ('PAY', 'RECEIVE') THEN '现金流') - 常见错误:在
base_voucher里 LEFT JOIN 科目表补科目名称——这会让下游无法按需选择不同会计准则下的科目映射,也导致 base 层耦合了维度逻辑
dm_ 视图如何安全补全维度与核算口径
dm_ 层才是财务逻辑真正开始分层的地方,核心任务是把 base 数据“宽表化”,但必须守住两条线:不带动态筛选、不越权聚合。
- 允许:LEFT JOIN
dim_account补会计科目全路径、JOINdim_cost_center补成本中心层级、用TO_CHAR(doc_date, 'YYYY-MM')封装会计期间(不是自然月) - 禁止:
WHERE fiscal_year = '2024'、AND company_code = 'CN01'、GROUP BY account_code, cost_center - 性能关键点:所有 JOIN 条件字段(如
voucher_id,account_id)必须在 base 表和维表上建索引;避免在 dm_ 层用SELECT *,只取下游报表真需要的字段,比如凭证行级明细不需要cost_center_desc就别拉
rpt_ 视图怎么封装“可交付”的核算结果
rpt_ 是唯一可以带业务语义、可读字段和轻量计算的视图,但它不是“终点”,而是交付给 BI 或报表工具的稳定接口。
- 必须做:用
CASE WHEN GROUPING(account_level1) = 1 THEN '总计' ELSE account_level1 END AS display_account处理 ROLLUP 占位符;用COALESCE(currency_rate, 1.0)填补汇率缺失;字段名带业务前缀,如local_amt、usd_amt、is_adjustment_flag - 禁止:写死时间范围(
stat_period BETWEEN '2024-01' AND '2024-12')、硬编码审批状态(WHERE approval_status = 'APPROVED')、嵌套调用另一个 rpt_ 视图 - 容易被忽略的坑:
rpt_profit_loss若定义为SELECT * FROM dm_voucher_summary WHERE doc_date >= '2024-01-01',那它就不是 rpt 层,而是伪 base 层——下游查 2023 年数据时会静默返回空,而不是报错或给提示
为什么不能用嵌套视图实现“自动结转”或“多期对比”
财务里常见的“本年累计”“年初余额”“同比变动”看着像能用视图嵌套解决,但实际一跑就出问题:数据库展开后生成超长执行计划,优化器放弃下推过滤,IO 翻倍,且变更定位极难。
- 真实现象:视图
rpt_balance_ytd依赖dm_balance_snapshot,后者又 JOIN 了 5 张历史快照表;某天基表加了个字段,rpt_balance_ytd报错column "xxx" does not exist,但错误栈根本没提dm_balance_snapshot,排查要手动翻三层定义 - 正确做法:把“期初余额”“本期发生”“期末余额”拆成三个独立 rpt_ 视图,命名明确(
rpt_balance_opening、rpt_balance_current、rpt_balance_closing),让报表 SQL 自己JOIN ON period = ?或用 CTE 关联 - 底线提醒:只要视图定义里出现
UNION ALL、LAG()、ROW_NUMBER()或跨期JOIN,就已超出视图能力边界,该上存储过程或应用层计算了

















