数据仓库实践:使用SQL汇总BOM数据分析维度

背景

物料清单(Bill of Materials, BOM)主数据是指在制造业中,用于描述产品结构、组件和制造工艺的核心数据。

将BOM主数据引入数仓,可以保证数据流转和分析过程中保证与制造过程的一致,以确保分析结果的准确。

在ERP数据库或者PLM数据库中保存的BOM清单一般是一对多关系,

但从企业全局看,上阶料产品和下阶料(子阶料)一般是多对多的关系;

需要兼容的关系一般有半成品、替代料、共用料等;

BOM数据分析维度:重新汇总拓展BOM

在这个重新汇总的BOM维度数据中,需要兼容的汇总逻辑有:

BOM 的层次结构

全局BOM中的环形结构

替代料

企业全局下材料和产品组成用量总和

BOM 层次结构

平常的BOM明细数据中一般只有一层,这是为了保证单一和准确,

在Java、Python等可以使用循环递归的语言环境下,计算和存储成本非常低,

但对于常用SQL的数据分析场景下,SQL不擅长递归,所以一般需要将递归汇总的结果直接落地成表。

BOM 的环形结构

这里的环形结构是指在循环展BOM层次时,同一成品会通过不同路径同一材料。

在这中情况下,需要根据实际的情况求和或者去重。

替代料

全局BOM中不只是主料与成品有关系,替代料也是汇总计算的关键一环,所以在汇总代码中也要考虑。

数据读取:BOM明细生成

结构

手动模仿一般情况下BOM的数据明细表结构,一个是主料明细,一个是替代料明细;

以下是对应表DDL语句

create table if not exists clip_ods.bom_alternative
(
    product_code     varchar(100) not null comment '产品物料料号',
    main_code        varchar(100) not null comment '主料物料料号',
    alternative_code varchar(100) not null comment '替代物料料号',
    qpa              double       null comment '主料组成用量',
    alternative_qpa  double       null comment '替代用量',
    enable           varchar(100) null comment '启用状态',
    primary key (product_code, main_code, alternative_code)
) comment '替代料BOM明细';

create table if not exists clip_ods.bom_main
(
    product_code  varchar(100) not null comment '产品物料料号',
    material_code varchar(100) not null comment '子阶物料料号',
    qpa           double       null comment '组成用量',
    enable        varchar(100) null comment '启用状态',
    primary key (product_code, material_code)
) comment '主料BOM明细';

部分数据

主料

替代料

基础结构汇总

在使用BOM汇总时,一般需要BOM明细中的所有关系,

所以从局部看,单个成品与材料是一对多关系,

从全局看成品,半成品和最底层材料,是多对多关系。

所以我们需要将所有的关系找出来,然后经过一番去重和重新汇总。

以下是部分代码

# 基础结构汇总
select product_code,
       material_code,
       qpa,
       main,
       enable
from (select product_code,
             material_code,
             qpa,
             main,
             enable,
             # 汇总之后有一部分product_code, material_code会因为同时状态为有效和无效
             # 这里状态有效优先
             row_number() over (partition by product_code, material_code,enable order by case when main = 'Y' then 1 else 2 end) rn
      from (select product_code,
                   material_code,
                   # 相同产品和相同材料的情况下按product_code, material_code求和
                   # 这样能够得出在企业全局下此产品使用此材料的组成用量
                   # ,可按实际情况灵活使用
                   sum(qpa) as qpa,
                   'Y'         main,
                   enable
            from clip_ods.bom_main
            where enable = 'Y'
            group by product_code,
                     material_code,
                     enable
            # 数据会被去重,可按实际情况灵活使用
            union
            select product_code,
                   alternative_code,
                   qpa / alternative_qpa as qpa,
                   'N'                      main,
                   enable
            from clip_ods.bom_alternative
            where enable = 'Y') bom_ori) bom_unique
where rn = 1

递归

正常BOM不会只有一层,因为材料的上阶料不一定直接是最终成品,有可能是半成品,会形成【材料-半成品-成品】的关系,

所以为了完整展出每层关系,我们需要使用递归的方式,将基础结构多次汇总,

示例中将使用MySQL 语法,

除了用于循环的部分关键词,汇总的语句代码是最基础的SQL代码,可以使用其他的数据库方言翻译,比如Oracle。

建立存储过程

create
    definer = root@localhost procedure recursion_split_bom()
BEGIN
    # 定义递归层级
    DECLARE iter integer default 1;
    # 删除原有数据,重新写入全量数据
    truncate table clip_dwh.dim_bom;

    commit;
    # 基础结构直接写入
    insert into clip_dwh.dim_bom(product_code, material_code, bom_hierarchy, lvl, qpa, main, enable)
    select product_code,
           material_code,
           concat(product_code, '_', material_code) bom_hierarchy,
           iter                                     lvl,
           qpa,
           main,
           enable
    from (select product_code,
                 material_code,
                 qpa,
                 main,
                 enable
          from (select product_code,
                       material_code,
                       qpa,
                       main,
                       enable,
                       row_number() over (partition by product_code, material_code,enable order by case when main = 'Y' then 1 else 2 end) rn
                from (select product_code,
                             material_code,
                             sum(qpa) as qpa,
                             'Y'         main,
                             enable
                      from clip_ods.bom_main
                      where enable = 'Y'
                      group by product_code,
                               material_code,
                               enable
                      union
                      select product_code,
                             alternative_code,
                             qpa / alternative_qpa as qpa,
                             'N'                      main,
                             enable
                      from clip_ods.bom_alternative
                      where enable = 'Y') bom_ori) bom_unique
          where rn = 1) bom_basic;
    commit;
    
    # 每一次基础结构和已经写入的数据join关联都能关联出新一层
    # iter 循环层数视实际数据而定,一般是最深BOM的层数
    WHILE iter <= 10
        DO
            set iter = iter + 1;
            insert into clip_dwh.dim_bom(product_code, material_code, bom_hierarchy, lvl, qpa, main, enable)
            select bom_result.product_code,
                   bom_iter.material_code,
                   concat(bom_result.bom_hierarchy, '_', bom_iter.material_code) as bom_hierarchy,
                   iter                                                          as lvl,
                   bom_result.qpa * bom_iter.qpa                                 as qpa,
                   IF(bom_result.main = 'N', 'N', bom_iter.main)                 as main,
                   IF(bom_result.enable = 'N', 'N', bom_iter.enable)             as enable
            from (select product_code,
                         material_code,
                         qpa,
                         main,
                         enable
                  from (select product_code,
                               material_code,
                               qpa,
                               main,
                               enable,
                               row_number() over (partition by product_code, material_code,enable order by case when main = 'Y' then 1 else 2 end) rn
                        from (select product_code,
                                     material_code,
                                     sum(qpa) as qpa,
                                     'Y'         main,
                                     enable
                              from clip_ods.bom_main
                              where enable = 'Y'
                              group by product_code,
                                       material_code,
                                       enable
                              union
                              select product_code,
                                     alternative_code,
                                     qpa / alternative_qpa as qpa,
                                     'N'                      main,
                                     enable
                              from clip_ods.bom_alternative
                              where enable = 'Y') bom_ori) bom_unique
                  where rn = 1) bom_iter
                     inner join(select product_code,
                                       material_code,
                                       bom_hierarchy,
                                       lvl,
                                       qpa,
                                       main,
                                       enable
                                from clip_dwh.dim_bom) bom_result
                               on bom_iter.product_code = bom_result.material_code
            where bom_result.lvl >= iter - 1;
        END WHILE;
    commit;
END;

部分结果

结果数据结构

create table clip_dwh.dim_bom
(
    product_code  varchar(100) not null comment '上阶料',
    material_code varchar(100) not null comment '下阶料',
    bom_hierarchy varchar(260) not null comment 'BOM 分层',
    lvl           int          null comment '分层等级',
    qpa           double       null comment '组成用量',
    main          varchar(100) null comment '主料',
    enable        varchar(100) null comment '启用',
    primary key (product_code, material_code, bom_hierarchy)
);
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

The_Singing_Towers

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值