- 输入数据
- 采购订单、物料主数据、历史价格
- 核心方法
- 价格偏离度 + 同类比较
- 输出物
- 价格异常清单与多付金额
采购价格异常,指同一物料本期的采购单价明显高于它的历史价格。靠人工翻历史订单,很难看全这一单比过去贵了多少。这个模型先用去年的采购订单算出每种物料的基准价,再把今年每一笔订单的单价和基准价比较,偏高超过阈值的列出来,并估算多付了多少钱。结果是需要解释原因的订单清单,不代表一定有问题。
适用场景
- 采购月度复盘:每月跑一遍,把明显偏高的订单交给采购员说明原因。
- 采购监督:国企、集团采购管理部门对下属单位的采购价格做全量筛查。
- 供应商谈判准备:先看哪些物料的价格涨得最多、多付金额最大。
输入数据要求
一张采购订单行表,每行一种物料的一次采购。字段名可以不同,在工作流第 1 步改成下表的名称即可。
| 字段 | 是否必需 | 类型 | 说明 |
|---|---|---|---|
| 订单号 | 必需 | 文本 | 每行唯一 |
| 下单日期 | 必需 | 日期 | 格式 yyyy-MM-dd,用于划分基准期和检查期 |
| 物料编码 | 必需 | 文本 | 同一物料必须编码一致,名称不参与比较 |
| 数量 | 必需 | 数值 | 用于计算多付金额 |
| 单价 | 必需 | 数值 | 统一口径,示例数据为不含税单价 |
| 状态 | 可选 | 文本 | 值为「已取消」的订单会被排除 |
| 物料名称、单位、供应商编码、供应商名称、采购员 | 可选 | 文本 | 不参与判断,只带到结果里方便核实 |
同一物料的单价口径必须一致。含税与不含税、不同计量单位混在一起时,先在数据源里统一,否则偏离率没有意义。
判定规则
基准期取 2025 全年,检查期取 2026 年上半年,两个时间段都可以在参数里改。
| 步骤 | 规则 |
|---|---|
| 基准价 | 每种物料在基准期内全部有效订单单价的中位数;基准期订单少于 3 笔的物料不判断 |
| 偏离率 | 单价 ÷ 基准价 − 1,保留四位小数后再比较 |
| 多付金额 | (单价 − 基准价)× 数量,保留两位小数 |
| 等级 | 条件 | 风险 |
|---|---|---|
| 明显偏高 | 偏离率 ≥ 50% | 高 |
| 偏高 | 20% ≤ 偏离率 < 50% | 中 |
用中位数而不是平均数,是为了不让基准期里偶尔一两笔特别贵或特别便宜的订单把基准价带偏。只看偏高,价格偏低的订单不输出。
工作流步骤
节点按 KNIME 英文名给出,iModel 中文界面的节点名以你所用的版本为准。示例数据已放在工作流内,导入后直接执行即可。
- 读取采购订单CSV Reader
- 排除取消单、转换日期Row Filter · String to Date&Time
- 拆分基准期与检查期Date&Time-based Row Filter
- 按物料算基准价(中位数)GroupBy
- 关联回检查期订单Joiner
- 算偏离率与多付金额Math Formula
- 按阈值判定Rule Engine
- 输出异常订单CSV Writer
- 读取采购订单CSV Reader
读取
purchase_orders.csv,把「物料编码」「订单号」设为文本。 - 排除无效订单、转换日期Row FilterString to Date&Time
保留「状态」不等于「已取消」且「单价」大于 0 的行;「下单日期」转为日期类型。
- 拆分基准期与检查期Date&Time-based Row Filter
用两个节点分别筛出基准期(2025-01-01 至 2025-12-31)与检查期(2026-01-01 至 2026-06-30)的订单,日期取自参数。
- 计算基准价GroupByRow Filter
基准期数据按「物料编码」分组,单价取 Median 命名为「基准价」,订单号取 Count 命名为「基准笔数」;再只保留「基准笔数」大于等于 3 的物料。
- 关联到检查期订单Joiner
检查期订单与基准价按「物料编码」内连接,没有基准价的物料在这一步自然被去掉。
- 计算偏离率与多付金额Math Formula
「偏离率」:
round(($单价$ / $基准价$ - 1) * 10000) / 10000
「多付金额」:round(($单价$ - $基准价$) * $数量$ * 100) / 100 - 按阈值判定Rule Engine
新增列「风险等级」,规则依次为:
$偏离率$ >= $${D高阈值}$$ => "高"$偏离率$ >= $${D中阈值}$$ => "中" - 整理输出Row FilterSorterCSV Writer
去掉「风险等级」为空的行,按「多付金额」从大到小排序,写出结果文件。
参数
| 参数 | 默认值 | 作用 | 怎么调 |
|---|---|---|---|
| 基准期 | 2025-01-01 至 2025-12-31 | 计算基准价的时间段 | 物料价格季节性明显时,改用去年同期 |
| 检查期 | 2026-01-01 至 2026-06-30 | 要检查的订单时间段 | 按月跑时改为当月 |
| 中阈值 | 0.2 | 偏高的起点 | 物料价格波动大的品类可以放宽到 0.3 |
| 高阈值 | 0.5 | 明显偏高的起点 | 与中阈值保持一定间距 |
| 最少基准笔数 | 3 | 基准价可信的最低订单数 | 低频采购物料多时可降到 2,但基准价会更不稳定 |
用示例数据验证
运行后结果应与示例数据包里的 expected_result.csv 完全一致。示例数据中偏离率恰好等于 20% 和 50% 的订单都应被标出,恰好低于 20% 的不应被标出。
| 订单号 | 物料编码 | 物料名称 | 单价 | 基准价 | 偏离率 | 多付金额 | 风险 |
|---|---|---|---|---|---|---|---|
| PO00293 | M0010 | 轴承-A10 | 1,562.45 | 762.17 | 105.00% | 248,086.80 | 高 |
| PO00210 | M0008 | 气泡膜-A8 | 1,090.74 | 852.14 | 28.00% | 52,014.80 | 中 |
| PO00312 | M0005 | 防静电手套-A5 | 1,295.03 | 863.35 | 50.00% | 50,506.56 | 高 |
| PO00304 | M0001 | 包装纸箱-A1 | 530.61 | 327.54 | 62.00% | 32,288.13 | 高 |
| PO00226 | M0014 | 工业润滑油-B4 | 295.14 | 245.95 | 20.00% | 19,971.14 | 中 |
| PO00291 | M0019 | 电缆-B9 | 406.20 | 282.08 | 44.00% | 11,543.16 | 中 |
| PO00300 | M0003 | 不锈钢螺栓-A3 | 448.23 | 332.02 | 35.00% | 1,278.31 | 中 |
另有四类不应被标出的数据:偏离率 19.9% 的订单、价格下降 40% 的订单、单价明显偏高但已取消的订单、基准期只有 2 笔订单的物料。
适用边界
- 基准价反映的是历史价格,不是市场价。如果去年就一直买贵了,这个模型看不出来;需要和外部报价或其他单位的采购价比较。
- 原材料大幅涨价时会整体偏高。遇到铜、钢、纸浆这类大宗价格普涨,应调整基准期或按品类放宽阈值,否则疑点过多。
- 规格不同但编码相同会误报。物料编码颗粒度太粗时,先细化主数据。
- 数量折扣不在考虑范围内。小批量采购单价本来就高,必要时按数量区间分别计算基准价。
常见问题
为什么用中位数,不用平均价?
平均价会被个别极端订单拉高或拉低,中位数更接近「通常买多少钱」。
能不能和多家供应商的报价比较?
可以另建一个分支:检查期内按「物料编码 + 月份」分组,比较同月不同供应商的单价差,这是另一种口径,建议作为补充而不是替代。
数据在 ERP 里,怎么接?
把第 1 步换成数据库读取节点,查询出上表的字段即可,后面的步骤不用改。
采购价格异常能直接认定采购人员有问题吗?
不能。价格偏高可能有交期紧急、规格变更、供应商调整等正常原因,结果用于发起核实。

