库存的两套表达
库存建模有两种互补表达:日快照记录「某仓某 SKU 在某日结账时还剩多少」,事件流水记录「入库、出库、调拨、盘点调整各发生了什么」。快照回答时点问题、体积可控;流水回答问题归因、可追溯到单据。供应链对账要求两者能互相推导:期末 = 期初 + 入库 - 出库 + 盘点调整。
基本信息
| 快照粒度 | SKU 乘以仓库乘以日,可按批次与库位细分 |
|---|---|
| 流水粒度 | 单据行级事件,带源单号与业务类型 |
| 常见平台 | Hive 与 Spark SQL 分区表,Doris 明细表做加速 |
| 勾稽公式 | 期末 = 期初 + 入库 - 出库 + 盘点调整 |
| 写入方式 | 快照按日分区覆盖,流水按事件时间分区追加 |
典型使用场景
- 库存日报与周转:按 SKU 与仓库看期末库存,快照表直接聚合,不必扫全量流水
- 库存健康分析:临期、滞销、超储要落到批次与库龄,快照加批次维度即可计算
- 出入库追溯:业务追问某天库存为何下降时,回到流水逐笔核对单据
- 成本核算:移动加权平均需要期初金额与入库金额,快照与流水都要具备
- 日终对账:每日跑勾稽任务,不平就说明存在未入账的调整或漏传单据
日快照建表与勾稽
日快照建表与勾稽
-- Hive/Spark SQL:库存日快照,按业务日期分区
CREATE TABLE fact_inventory_daily (
sku_id string COMMENT '商品编码',
warehouse_id string COMMENT '仓库编码',
open_qty bigint COMMENT '期初库存',
in_qty bigint COMMENT '当日入库',
out_qty bigint COMMENT '当日出库',
adj_qty bigint COMMENT '盘点调整',
close_qty bigint COMMENT '期末库存'
) PARTITIONED BY (dt string) STORED AS ORC;
-- 期末必须可推导,不平说明有动作没有入账
SELECT count(1) AS bad_cnt FROM fact_inventory_daily
WHERE dt = '2026-10-12'
AND close_qty <> open_qty + in_qty - out_qty + adj_qty;快照与流水对照
| 对比项 | 日快照 | 事件流水 |
|---|---|---|
| 粒度 | SKU 乘以仓库乘以日 | 单据行级事件 |
| 回答的问题 | 某时点有多少 | 为什么会变成这么多 |
| 数据量 | 小,可按分区裁剪 | 大,需要归档与冷热分层 |
| 典型用途 | 水位、周转、日报 | 追溯、成本、审计 |
| 常见坑 | 调整不入流水就永远对不平 | 只存流水算时点库存成本高 |
盘点调整必须作为流水入账:只在快照里改数字,流水永远解释不了差异,下一次对账时同一个缺口又会被当成新问题重新排查。
负库存要有阻断而不只是告警:负数通常来自先出后入或单据漏传,直接进快照会同时污染周转率与成本指标,且月末盘点的补账工作量极大。