必须用WITH RECURSIVE而非多层嵌套子查询处理BOM树形结构,因其可动态展开任意深度、避免硬编码层级、防止漏数据;嵌套子查询不可扩展、难调试、易因深度超限漏查且性能爆炸。

多层嵌套子查询处理BOM树形结构,本质上是在硬编码层级——它不可扩展、难调试、一改就崩。真要查完整BOM(比如某主件的所有子件、子子件、直到末级物料),必须用 WITH RECURSIVE,而不是靠 (SELECT ... FROM (SELECT ...)) 套娃。
为什么嵌套子查询撑不住BOM查询
典型错误是写 3–4 层 FROM 子查询或 JOIN 带子查询,试图“展开”3级BOM:
- 每加一层,SQL长度翻倍,缩进混乱,别名极易冲突(比如两层都用了
t1) - 一旦BOM实际深度超预设(比如第5层突然出现新子件),结果直接漏数据
- SQL Server 或 MySQL 8.0+ 优化器可能把嵌套子查询重写成多次扫描,
EXPLAIN显示 nested loop 次数爆炸 -
WHERE中用相关子查询判断“是否为末级”,会导致外层每行都执行一次内层,10万行BOM表可能触发百万次子查询
SQL Server / MySQL 8.0+ 必须用递归CTE的写法
锚点 + 递归成员是唯一可靠路径。以 wu_bom 表为例(字段:ID, plan, pact, amount),假设 pact 指向上级 ID:
WITH RECURSIVE bom_tree AS ( -- 锚点:从顶层主件开始(pact 为空或为自身ID) SELECT ID, plan, pact, amount, 1 AS level FROM wu_bom WHERE plan = 'MAIN-2026-A' AND (pact IS NULL OR pact = ID) <p>UNION ALL</p><p>-- 递归:找所有直接子件,并逐层向下 SELECT b.ID, b.plan, b.pact, b.amount, bt.level + 1 FROM wu_bom b INNER JOIN bom_tree bt ON b.pact = bt.ID WHERE bt.level < 32 -- 防死循环,SQL Server 默认栈深100,MySQL 可设 max_recursion_depth ) SELECT * FROM bom_tree ORDER BY level, plan;
-
UNION ALL是强制要求,不能用UNION(去重开销大且无意义) - 锚点里必须过滤出明确起点,不能写
WHERE 1=1然后靠递归过滤——否则第一轮就扫全表 -
WHERE bt.level < 32这类守卫条件建议显式加上,尤其当BOM存在脏数据(如自引用、环形引用)时
容易被忽略的兼容性细节
不同数据库对递归CTE的支持差异不小,不注意会报错或行为异常:
- SQL Server 支持
MAXRECURSION提示,但必须写在主查询末尾:OPTION (MAXRECURSION 50),不能放在 CTE 内部 - MySQL 8.0+ 要提前设置:
SET SESSION cte_max_recursion_depth = 500;,否则默认 1000,超限直接ERROR 3636 - PostgreSQL 不限制深度,但栈溢出报
ERROR: stack depth limit exceeded,需调statement_timeout或检查循环 - 所有数据库都不允许在递归部分的
SELECT里再嵌套另一个 CTE —— 即WITH不能出现在递归支路的子查询中
真正棘手的从来不是语法,而是BOM数据本身:pact 字段空值含义是否统一?是否存在跨版本混用的 plan 编码?这些业务逻辑漏洞,递归CTE不会帮你发现,但会让错误结果看起来特别“合理”。

















