3. 采购业务数据源分析:ERP系统核心表的数据特征与关系

采购支出数据仓库的基石是来自企业ERP系统的多个业务数据源。这些数据源并非孤立存在,而是通过主键与外键紧密关联,共同描述了从采购申请到付款结算的完整业务闭环。本章将深入分析采购订单表、供应商主数据、物料主数据以及发票数据的核心数据特征与实体关系。

3.1 数据源全景概览

在典型的ERP系统中,采购域涉及以下核心数据对象。理解它们的粒度、时效性及关联方式是构建星型模型的前提。

数据源/表 业务含义 记录粒度 数据特征
采购订单表 (PO_Header/Item) 记录采购交易的核心单据,包含头信息与行项目 每张订单的每个物料行 高频更新,包含数量、单价、交货日期、状态
供应商主数据 (Vendor Master) 供应商的基本信息与财务/采购视图 每个供应商一条记录 低频变更,维度属性丰富(名称、分类、付款条款)
物料主数据 (Material Master) 采购物料的描述、分类与属性 每个物料编码一条记录 低频变更,是品类分类的核心来源
发票数据 (Invoice Data) 供应商开具的财务凭证,用于校验与付款 每张发票的每个行项目 财务级数据,包含税额、折扣、付款状态
ETL设计提示: 采购订单表是事实表的核心来源,其行项目粒度决定了支出分析的最小颗粒度。供应商与物料主数据作为维度表,其缓慢变化维度(SCD)策略直接影响历史趋势分析的准确性。

3.2 核心数据源详细特征

3.2.1 采购订单表 (Purchase Order)

采购订单表是采购流程的起点,通常分为订单头和订单行两部分。订单头记录全局信息(如供应商、采购组织、总金额),订单行记录具体物料、数量、单价和交货计划。

  • 关键字段: PO编号、行项目号、物料编码、供应商编码、订单数量、净价、货币、交货日期、采购组、工厂代码。
  • 数据特征: 数据量大,每日增量明显;存在订单变更历史(如修改数量、价格);状态字段复杂(已创建、已审批、已收货、已开票、已关闭)。
  • ETL挑战: 需要处理订单行拆分、变更追踪以及未清订单的周期性快照。

3.2.2 供应商主数据 (Vendor Master)

供应商主数据是采购分析的核心维度之一,决定了供应商绩效评估和支出集中度分析的质量。

  • 关键字段: 供应商编码、名称、国家、地区、行业分类、付款条款、付款方式、信用等级、采购组织视图。
  • 数据特征: 相对稳定,但存在多视图(财务视图、采购视图);部分字段(如分类)可能随时间调整。
  • ETL挑战: 需要处理供应商合并(如集团内供应商统一编码)、地址变更以及分类调整的SCD策略。

3.2.3 物料主数据 (Material Master)

物料主数据是品类分类分析的基石。它定义了“买的是什么”,是连接采购订单与财务分类账的桥梁。

  • 关键字段: 物料编码、物料描述、物料组、产品层级、基本计量单位、采购组、物料类型(原材料、半成品、服务)。
  • 数据特征: 数据量适中,但层级关系复杂(如物料组 -> 产品大类 -> 品类);描述信息可能存在不规范(如缩写、拼写错误)。
  • ETL挑战: 需要建立标准化的品类分类映射表,清洗物料描述,处理一物多码或一码多物的情况。

3.2.4 发票数据 (Invoice Data)

发票数据是财务付款的依据,也是校验采购订单执行准确性的关键。它通常与采购订单行、收货单行进行“三单匹配”。

  • 关键字段: 发票编号、发票行号、PO编号、PO行号、发票数量、发票金额、税额、折扣、付款日期、发票状态。
  • 数据特征: 数据量略小于采购订单;时间滞后(发票通常在收货后开具);存在红冲发票和贷项凭证。
  • ETL挑战: 需要处理发票与PO的匹配逻辑,识别价格差异和数量差异,以及处理跨月发票的账期归属。

3.3 数据源实体关系 (ER) 分析

下图展示了采购业务数据源之间的核心实体关系。理解这些关系是设计数据仓库事实表与维度表关联逻辑的基础。

采购业务数据源核心实体关系图 (ER) 供应商主数据 (Vendor Master) 物料主数据 (Material Master) 采购订单表 (PO Header + PO Item) 发票数据 (Invoice Header + Item) 外键: Vendor_Code 外键: Material_Code 外键: PO_Number + PO_Item 说明:采购订单表是核心事实,通过外键关联供应商与物料维度;发票数据通过PO编号与行号关联采购订单,形成“三单匹配”闭环。

3.4 数据关系与ETL映射策略

在数据仓库建模中,上述源表关系将映射为星型模型。以下是关键映射规则与ETL处理策略。

源表关系 数据仓库映射 ETL处理策略
采购订单行 → 供应商主数据 事实表.PO_Line → 维度表.Dim_Vendor 通过供应商编码关联;处理供应商合并(SCD Type 2)
采购订单行 → 物料主数据 事实表.PO_Line → 维度表.Dim_Material 通过物料编码关联;建立品类分类层级映射表
发票数据 → 采购订单行 事实表.Invoice_Line → 事实表.PO_Line 通过PO编号+行号进行精确匹配;处理差异与红冲
采购订单头 → 采购订单行 事实表.PO_Line → 维度表.Dim_PO_Header 通过PO编号关联;将头信息(如采购组)下钻到行项目
关键设计原则: 采购订单行项目表是支出分析的事实表主体。供应商和物料维度表应包含丰富的属性层级(如供应商所属集团、物料所属品类),以便支持上卷和下钻分析。发票数据作为滞后事实,应单独建立事实表或作为退化维度处理,避免与采购订单事实表过度耦合导致ETL复杂度爆炸。

3.5 数据质量与常见问题

在数据源接入阶段,ETL开发人员必须关注以下常见数据质量问题,这些问题直接影响品类分类趋势分析的准确性。

  • 供应商数据不一致: 同一供应商在不同系统或不同时间点存在多个编码(如“华为技术有限公司”与“华为技术”)。
    对策: 建立供应商统一编码映射表,在ETL阶段进行清洗与合并。
  • 物料分类缺失或错误: 物料主数据中的“物料组”字段为空,或分类层级不完整。
    对策: 基于物料描述进行关键词匹配或机器学习分类,补充缺失的品类信息。
  • 发票与PO匹配失败: 发票行引用的PO编号或行号在源系统中已被修改或删除。
    对策: 采用外连接保留未匹配记录,并记录异常日志供业务核对。
  • 货币与单位不统一: 采购订单使用本位币,发票使用外币,导致金额计算错误。
    对策: 在ETL中统一转换为标准货币(如人民币),并记录原始汇率。
避坑指南: 不要直接使用ERP源系统的“最后修改日期”作为增量抽取的唯一依据。部分ERP表在修改主字段时不会更新该时间戳。建议结合“创建日期”、“修改日期”以及CDC(变更数据捕获)日志共同判断增量数据。

3.6 数据源接入清单示例

以下是一个标准的数据源接入配置清单,用于指导ETL开发人员从ERP系统抽取采购相关数据。

-- 数据源接入配置清单 (示例)
-- 目标: 构建采购支出事实表 (FACT_PURCHASE_SPEND)

-- 1. 抽取采购订单行项目
SELECT
    po_header.po_number,
    po_item.item_number,
    po_item.material_code,
    po_header.vendor_code,
    po_item.order_quantity,
    po_item.net_price,
    po_item.currency,
    po_header.po_create_date,
    po_header.purchasing_group
FROM
    erp.po_header AS po_header
    INNER JOIN erp.po_item AS po_item
        ON po_header.po_number = po_item.po_number
WHERE
    po_header.last_change_date >= '${last_run_date}';

-- 2. 抽取供应商维度 (SCD Type 2)
SELECT
    vendor_code,
    vendor_name,
    vendor_country,
    vendor_industry,
    effective_date,
    expiry_date,
    is_current
FROM
    erp.vendor_master
WHERE
    last_change_date >= '${last_run_date}';

-- 3. 抽取物料维度 (含品类映射)
SELECT
    mat.material_code,
    mat.material_description,
    mat.material_group,
    cat.category_name AS standard_category,
    cat.category_level
FROM
    erp.material_master AS mat
    LEFT JOIN dw.dim_category_mapping AS cat
        ON mat.material_group = cat.source_group_code
WHERE
    mat.last_change_date >= '${last_run_date}';