You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用单条SQL查询获取物料清单(BOM)全层级数据

递归SQL查询全层级物料清单(BOM)

我经常碰到这种多层级嵌套的BOM查询需求,用**递归公共表表达式(WITH RECURSIVE)**绝对是最顺手的解决方案,几乎所有主流数据库(PostgreSQL、MySQL 8.0+、SQL Server、Oracle 11gR2+等)都支持这个语法,轻松搞定10层甚至15层的深度完全不在话下。

假设的BOM表结构

先定义一个典型的BOM表,我们叫它bom,核心字段包含:

  • parent_item: 父级物料编码(成品或半成品)
  • child_item: 子级物料编码(半成品或原材料)
  • quantity: 每个父物料所需的子物料数量

表结构的SQL定义(以MySQL为例):

CREATE TABLE bom (
    parent_item VARCHAR(50) NOT NULL,
    child_item VARCHAR(50) NOT NULL,
    quantity DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (parent_item, child_item)
);

示例测试数据

我们用你提到的物料'A'模拟多层嵌套结构:

-- A的子项:半成品B、原材料C
INSERT INTO bom VALUES ('A', 'B', 2), ('A', 'C', 1);
-- B的子项:半成品D、原材料E
INSERT INTO bom VALUES ('B', 'D', 3), ('B', 'E', 1);
-- D的子项:原材料F
INSERT INTO bom VALUES ('D', 'F', 5);

递归查询SQL实现

下面这条SQL会获取物料'A'从顶层到最底层的所有层级详情,还新增了level字段显示层级深度,方便你直观梳理嵌套关系:

WITH RECURSIVE bom_hierarchy AS (
    -- 锚点成员:起始的父物料(这里指定为'A')
    SELECT 
        parent_item,
        child_item,
        quantity,
        1 AS level  -- 顶层层级设为1
    FROM bom
    WHERE parent_item = 'A'
    
    UNION ALL
    
    -- 递归成员:循环关联下一层的子物料
    SELECT 
        b.parent_item,
        b.child_item,
        b.quantity,
        bh.level + 1 AS level
    FROM bom b
    JOIN bom_hierarchy bh ON b.parent_item = bh.child_item
)
-- 按层级排序输出结果
SELECT * FROM bom_hierarchy ORDER BY level, parent_item;

查询结果示例

执行后会得到清晰的层级关系表:

parent_itemchild_itemquantitylevel
AB2.001
AC1.001
BD3.002
BE1.002
DF5.003

关键注意事项

  • 避免循环引用:如果BOM里存在循环(比如A→B,B→A),递归会陷入无限循环。可以新增path字段记录遍历路径,排除重复物料:
    WITH RECURSIVE bom_hierarchy AS (
        SELECT 
            parent_item,
            child_item,
            quantity,
            1 AS level,
            CONCAT('>', parent_item, '>', child_item) AS path
        FROM bom
        WHERE parent_item = 'A'
        
        UNION ALL
        
        SELECT 
            b.parent_item,
            b.child_item,
            b.quantity,
            bh.level + 1 AS level,
            CONCAT(bh.path, '>', b.child_item) AS path
        FROM bom b
        JOIN bom_hierarchy bh ON b.parent_item = bh.child_item
        WHERE bh.path NOT LIKE CONCAT('%>', b.child_item, '%')
    )
    SELECT * FROM bom_hierarchy ORDER BY level;
    
  • 数据库兼容性:如果是MySQL 5.x这类不支持递归CTE的旧版本,可能需要用存储过程+循环的方式实现,但现在大部分企业都已经升级到支持递归CTE的版本了。

内容的提问来源于stack exchange,提问作者syedcic

火山引擎 最新活动