3. 批次关联数据模型设计
批次关联管理的核心不在于“记录一个批次号”,而在于把供应商、物料、批次、产品、订单这五类对象之间的多对多、层级与流向关系稳定地表达出来。如果数据模型设计不当,后续的追溯查询会退化为大量字符串匹配,既慢又容易断链。本节从实体关系出发,落到关键字段与数据库表结构,给出可直接参考的设计方案。
一、五个核心实体及其关系
先明确各实体的职责边界:供应商是批次的外部来源主体;物料是采购进来的原料或包材,属于“品类级”定义;批次是物料在某一时间、某一来源下的具体实例,是追溯的最小单元;产品是生产出来的成品品类;订单既包括采购订单(供应商→企业),也包括销售订单(企业→客户),是批次流动的业务凭据。
它们之间的关系可以概括为:一个供应商可供应多种物料,一种物料可来自多个供应商;一个物料对应多个批次,每个批次有唯一的来源供应商;批次经生产投料转化为产品批次,形成“父批次—子批次”的层级;订单则把批次与外部主体(供应商或客户)绑定,是正向追踪与反向追溯的入口。下图展示了这一实体关系与批次层级结构。
二、关键字段设计
批次号(batch_no)是业务主键,建议采用“类型码+日期+流水”的可读编码,例如原料批次 RM-20240312-001、成品批次 FG-20240315-002。批次号必须全局唯一,但不宜作为数据库主键,因为业务编码规则可能调整;应另设自增或雪花 ID 作为物理主键,批次号加唯一索引。
生产日期(production_date)与保质期(shelf_life_days)应分开存储,而不是只存到期日。原因是保质期可能因复检、工艺变更而调整,保留原始天数便于重算。到期日 expiry_date 可由两者推导,但建议冗余存储并建索引,因为追溯查询中“是否过期”是高频过滤条件。计算方式为:expiry_date = production_date + shelf_life_days,按自然日计算,需在字段注释中明确“含生产当日”或“不含”,避免跨系统口径不一致。
关联父批次(parent_batch_id)是层级追溯的关键。它指向同一张批次表的主键,形成自引用结构。一个成品批次通常由多个原料批次投料产生,因此严格来说父子关系是多对多,需要一张批次关联表(batch_relation)来承载,记录“父批次、子批次、投入量、关联类型(投料/分装/返工)”。若只在批次表上放一个 parent_batch_id,会丢失多原料投料信息,这是常见的设计缺陷。
此外,批次表还应包含:物料 ID 或产品 ID(二选一或通过类型字段区分)、供应商 ID(原料批次必填)、订单 ID(可空,用于关联业务凭据)、状态(待检/合格/不合格/冻结)、库位与数量。状态字段对召回演练尤其重要——冻结状态应能阻断出库。
三、数据库表设计原则
原则一:主数据与业务数据分离。供应商、物料、产品属于主数据,变更频率低;批次、订单属于业务数据,增长快。分表后,主数据可加缓存,业务表可独立分区。
原则二:批次表只存“实例属性”,关系放关联表。批次号、日期、状态是实例属性;父子关系、订单归属是关系,应拆到关联表,避免宽表膨胀和更新异常。
原则三:为追溯路径建索引。反向追溯(从成品查原料)依赖 parent_batch_id 或关联表的 child_batch_id 索引;正向追踪(从原料查流向)依赖 parent_batch_id 索引。两个方向都要覆盖。
原则四:保留审计字段。创建时间、创建人、更新时间是召回报告的必要证据,不可省略。
下面给出核心表的建表示例(以 MySQL 语法示意,字段类型可按实际库调整):
CREATE TABLE batch (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
batch_no VARCHAR(40) NOT NULL,
batch_type TINYINT NOT NULL COMMENT '1原料 2成品 3半成品',
material_id BIGINT NULL,
product_id BIGINT NULL,
supplier_id BIGINT NULL COMMENT '原料批次必填',
production_date DATE NOT NULL,
shelf_life_days INT NOT NULL,
expiry_date DATE NOT NULL,
status TINYINT NOT NULL DEFAULT 0 COMMENT '0待检 1合格 2不合格 3冻结',
created_at DATETIME NOT NULL,
UNIQUE KEY uk_batch_no (batch_no),
KEY idx_expiry (expiry_date),
KEY idx_supplier (supplier_id)
);
CREATE TABLE batch_relation (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
parent_batch_id BIGINT NOT NULL COMMENT '上游批次',
child_batch_id BIGINT NOT NULL COMMENT '下游批次',
relation_type TINYINT NOT NULL COMMENT '1投料 2分装 3返工',
input_qty DECIMAL(12,3) NULL,
created_at DATETIME NOT NULL,
KEY idx_parent (parent_batch_id),
KEY idx_child (child_batch_id)
);
四、一个可验证的追溯示例
假设原料批次 RM-20240312-001(生产日期 2024-03-12,保质期 180 天,到期日 2024-09-08)于 3 月 15 日投料生产成品批次 FG-20240315-002,投入量 500 kg。在 batch_relation 中写入一条记录:parent 为原料批次 ID,child 为成品批次 ID,relation_type=1,input_qty=500。当客户投诉成品批次时,以 child_batch_id 查关联表即可得到全部上游原料批次;再以原料批次查供应商,即完成反向追溯。反之,若某原料供应商通报质量问题,以 parent_batch_id 查关联表,可列出所有受影响成品批次及其订单,用于定向召回。
最后提醒两个易错点:一是保质期单位要统一为“天”,避免月、年混用导致到期日计算偏差;二是批次状态变更应留痕,建议增加状态变更日志表,否则召回时无法证明“该批次在何时被冻结”。数据模型一旦承载了这两点,批次关联管理才真正具备可审计、可追溯的工程价值。