化工企业的物料主数据,难点集中在同一种物料有多种纯度、规格、包装和危化属性,再叠上名称不统一、分类偏粗。可行的思路是用分类加关键特征项来定义唯一性,并把危化、批次这些属性一并纳入模型。
难点集中在四个地方
规格纯度多。 同一种化学品有工业级、试剂级、电子级;同一级别又分不同含量和形态。名称只写"某某酸",系统分不清是哪一种。
包装和计量单位多。 同一种原料可能袋装、桶装、吨袋,也可能槽车散装进厂。采购按吨,仓库按桶,生产按千克;液体原料要按密度在体积和重量之间换算,液碱这类原料财务还要按折百计量。
危化和合规属性多。 是否属于危险化学品、危险类别、UN 编号、是否易制毒或易制爆、储存和运输要求、安全技术说明书(SDS)。这些信息常常留在安全环保部门的台账里,没有挂到物料上。
多工厂各管各的。 同一种原料,各工厂叫法不同,批次规则、复检周期、储存条件也不同。集团要汇总用量和库存,先得判断是不是同一种物料。
分类体系怎么搭:大类看用途,中小类看属性
大类按用途分,方便采购、生产、财务对口;中小类按化学属性和形态分,方便挂特征项模板。大、中、小三级通常够用。
| 大类(按用途) | 中类(示例) | 小类(示例) |
|---|---|---|
| 原料 | 无机原料、有机原料 | 酸、碱、盐;醇、酮、酯 |
| 中间体 | 按工艺路线 | 按产品族 |
| 产成品 | 按产品族 | 按牌号 |
| 助剂与催化剂 | 助剂、催化剂 | 按功能 |
| 包材 | 桶、袋、吨袋 | 按材质和容量 |
| 备品备件 | 按设备类型 | 按部件 |
每个小类写清"什么进、什么不进",再挂一套特征项模板。
特征项模板的一个示例
以一种液体有机原料为例,以下取值均为示意:
| 特征项 | 示意值 | 进唯一性判断 | 填写规则 |
|---|---|---|---|
| 标准名称 | 乙酸乙酯 | 是 | 从标准名称库选,别名单独维护 |
| CAS 号 | 141-78-6 | 是 | 按格式校验 |
| 等级 | 工业级 | 是 | 下拉:工业级 / 试剂级 / 电子级 |
| 标称含量 | ≥99.5% | 是 | 填标称值,批次实测值不填这里 |
| 形态 | 液体 | 是 | 下拉 |
| 危险类别 | 易燃液体 | 否 | 按 CAS 号从危化品目录参照表带出 |
| UN 编号 | UN1173 | 否 | 按 CAS 号带出 |
| 易制毒 / 易制爆 | 否 | 否 | 下拉,安环部门确认 |
| 储存条件 | 阴凉通风,远离火源 | 否 | 文本模板 |
| 包装规格 | 200 L 桶;槽车散装 | 否 | 放包装子表,一个物料挂多种包装 |
| 基本计量单位 | 千克 | 否 | 全集团统一 |
| 批次管理 | 是 | 否 | 危化品默认启用 |
| 复检周期 | 按工厂设置 | 否 | 放工厂视图 |
查重规则怎么定
- 唯一性键=分类+关键特征项。 上表标"是"的几项全部一致,就是同一种物料,申请时直接拦下。
- 名称只做参考。 建别名库,"醋酸乙酯""乙酸乙酯"指向同一个标准名称。
- 包装分两种情况。 原料不同包装算同一个物料,包装放子表、写清换算;产成品如果按包装分别定价、分别出库,就按包装建销售物料,挂在同一个基础物料下面。
- 批次实测值不进物料主数据。 实测含量、生产日期、有效期属于批次,放进批次管理。
- 混合物没有单一 CAS 号,用配方号或技术规格书编号作关键特征项。
实施顺序
- 先盘危化品。 从安全环保台账出发,把危化品物料的 CAS 号、危险类别补齐。合规风险最大,优先处理。
- 定分类和特征项模板。 研发、采购、安环、生产一起定,每个小类指定一个数据负责人。
- 清洗存量。 按唯一性键分组,组内合并,留一个主码,其余做映射或冻结。
- 新增只走平台。 申请时按特征项查重,审批后分发到 ERP、WMS、LIMS 等系统。
- 定期出质量报告。 按唯一性、一致性、完整性、准确性检查,问题落到数据负责人。
附:特征项模板与危化属性的表设计
模板、值域与关键特征标记
sql
CREATE TABLE chem_feature_template (
template_id VARCHAR(32) PRIMARY KEY,
category_leaf VARCHAR(32) NOT NULL UNIQUE,
name_pattern VARCHAR(200) NOT NULL, -- 例:{名称}{等级}{含量}{形态}{包装}
CONSTRAINT ck_pattern CHECK (name_pattern LIKE '%{名称}%')
);
CREATE TABLE chem_feature_item (
template_id VARCHAR(32) NOT NULL REFERENCES chem_feature_template,
feature_code VARCHAR(32) NOT NULL,
feature_name VARCHAR(64) NOT NULL, -- 中文名、CAS 号、纯度、形态、包装
seq SMALLINT NOT NULL, -- 拼接顺序
required CHAR(1) NOT NULL,
key_feature CHAR(1) NOT NULL, -- 是否进唯一性判断
fill_type VARCHAR(12) NOT NULL, -- 手填、下拉、参照带出
value_domain VARCHAR(200), -- 字典编码或正则
PRIMARY KEY (template_id, feature_code),
CONSTRAINT ck_chem_seq UNIQUE (template_id, seq)
);
-- 参照表:危险类别、UN 编号由 CAS 号带出,不给手工填
CREATE TABLE chem_hazard_ref (
cas_no VARCHAR(24) PRIMARY KEY,
danger_class VARCHAR(32) NOT NULL,
un_no VARCHAR(16),
control_type VARCHAR(16), -- 易制毒、易制爆、普通
storage_rule VARCHAR(64),
catalog_ver VARCHAR(16) NOT NULL -- 危化品目录版本
);
fill_type 是这组设计里最实用的一列。危险类别、UN 编号标成参照带出,申请页面就锁死;目录换版本,只更新参照表,历史物料按当时版本留痕。
唯一性键
sql
UPDATE mdm_material m
SET uniq_key = md5(
m.category_leaf || '|' ||
(SELECT string_agg(f.feature_value, '|' ORDER BY f.seq)
FROM mdm_material_feature f
WHERE f.material_code = m.material_code AND f.key_feature = 'Y')
);
CREATE UNIQUE INDEX uk_chem_uniq
ON mdm_material (category_leaf, uniq_key) WHERE status <> '停用';
中文名、CAS 号、等级、标称含量、形态、包装形式进键;批次实测值不进。混合物没有单一 CAS 号,用配方号或技术规格书编号顶上关键特征项,键照样能算。
CAS 号校验
CAS 号形如 141-78-6,末位是校验位:前面各段数字从右往左依次乘 1、2、3,加和后对 10 取模。
python
def cas_valid(cas):
parts = cas.split('-')
if len(parts) != 3 or not parts[2].isdigit():
return False
body = parts[0] + parts[1]
if not body.isdigit() or not 2 <= len(body) <= 10:
return False
total = sum(int(d) * (i + 1) for i, d in enumerate(reversed(body)))
return total % 10 == int(parts[2])
正则只做粗筛 ^[0-9]{2,7}-[0-9]{2}-[0-9]$,校验位才是判真伪的那道。化工物料名目多,把错号挡在提交环节,比事后靠安环部门排查便宜得多。
危化属性的带出
sql
SELECT m.material_code, m.material_name, m.cas_no,
h.danger_class, h.un_no, h.control_type, h.storage_rule
FROM mdm_material m
LEFT JOIN chem_hazard_ref h ON h.cas_no = m.cas_no
WHERE m.status = '生效';
-- 没有 CAS 号或带不出危险类别的物料,就是待补清单
SELECT m.material_code, m.material_name
FROM mdm_material m
LEFT JOIN chem_hazard_ref h ON h.cas_no = m.cas_no
WHERE m.status = '生效' AND h.cas_no IS NULL;
第二条查询的结果直接当存量清洗的任务清单。危化品台账和物料档案的差额,通常是合规检查最关心的问题。
批次数据不进物料主表
sql
CREATE TABLE chem_material_batch (
material_code VARCHAR(32) NOT NULL REFERENCES mdm_material,
batch_no VARCHAR(64) NOT NULL,
plant_code VARCHAR(16) NOT NULL,
produce_date DATE,
expiry_date DATE,
assay_value DECIMAL(8,3), -- 实测含量
color_no VARCHAR(16), -- 色号
reinspect_date DATE, -- 复检到期
PRIMARY KEY (material_code, batch_no, plant_code)
);
-- 临期与超复检期的看板
SELECT material_code, batch_no, plant_code, expiry_date, reinspect_date
FROM chem_material_batch
WHERE expiry_date < DATE '2026-04-15' OR reinspect_date < CURRENT_DATE;
批次主键带上工厂:同一种原料在两个工厂的批次规则不同,不带工厂就会互相覆盖。复检周期写在工厂视图,批次到期按各自规则算。
说明:本文按企业主数据项目的实施经验整理,客户信息已做匿名处理,文中表结构、报文、编码和数字均为技术示意,仅作同行交流,不构成采购建议。